/*- * #%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.fiji.plugin; import ij.gui.GenericDialog; import ij.plugin.PlugIn; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import mpicbg.spim.data.SpimData; import mpicbg.spim.data.registration.ViewRegistration; import mpicbg.spim.data.registration.ViewRegistrations; import mpicbg.spim.data.registration.ViewTransform; import mpicbg.spim.data.registration.ViewTransformAffine; import mpicbg.spim.data.sequence.Angle; import mpicbg.spim.data.sequence.Channel; import mpicbg.spim.data.sequence.Illumination; 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.data.sequence.VoxelDimensions; import mpicbg.spim.io.IOFunctions; import net.imglib2.Dimensions; import net.imglib2.img.list.ListImg; import net.imglib2.img.list.ListImgFactory; import net.imglib2.img.list.ListRandomAccess; import net.imglib2.realtransform.AffineTransform3D; import net.imglib2.util.Util; import spim.fiji.ImgLib2Temp.Pair; import spim.fiji.ImgLib2Temp.ValuePair; import spim.fiji.plugin.apply.ApplyParameters; import spim.fiji.plugin.apply.BigDataViewerTransformationWindow; import spim.fiji.plugin.apply.ModelLink; import spim.fiji.plugin.queryXML.LoadParseQueryXML; import spim.fiji.plugin.util.GUIHelper; import spim.fiji.spimdata.SpimData2; import spim.fiji.spimdata.ViewSetupUtils; import spim.process.fusion.boundingbox.BigDataViewerBoundingBox; import spim.vecmath.Transform3D; import bdv.BigDataViewer; public class Apply_Transformation implements PlugIn { public static boolean defaultSameModelTimePoints = true; public static boolean defaultSameModelChannels = true; public static boolean defaultSameModelIlluminations = true; public static boolean defaultSameModelAngles = true; public static int defaultModel = 2; public static int defaultDefineAs = 1; public static int defaultApplyTo = 2; public static String[] applyToChoice = new String[]{ "Identity transform (removes any existing transforms)", "Calibration (removes any existing transforms)", "Current view transformations (appends to current transforms)" }; public static String[] modelChoice = new String[]{ "Identity (no transformation)", "Translation", "Rigid", "Affine" }; public static String[] defineAsChoice = new String[] { "Matrix", "Rotation around axis", "Interactively using the BigDataViewer" }; public String[] axesChoice = new String[] { "x-axis", "y-axis", "z-axis" }; public static int[] defaultAxis = null; public static double[] defaultDegrees = null; public static ArrayList< double[] > defaultModels; @Override public void run( final String arg0 ) { // ask for everything final LoadParseQueryXML result = new LoadParseQueryXML(); if ( !result.queryXML( "applying a transformation", "Apply to", true, true, true, true ) ) return; final SpimData2 data = result.getData(); final List< ViewId > viewIds = SpimData2.getAllViewIdsSorted( result.getData(), result.getViewSetupsToProcess(), result.getTimePointsToProcess() ); final ApplyParameters params = queryParams( data, viewIds ); if ( params == null ) return; final Map< ViewDescription, Pair< double[], String > > modelLinks; // query models and apply them if ( params.defineAs == 0 ) // matrix modelLinks = queryString( data, viewIds, params ); else if ( params.defineAs == 1 ) //Rotation around axis modelLinks = queryRotationAxis( data, viewIds, params ); else // Interactively using the BigDataViewer modelLinks = queryBigDataViewer( data, viewIds, params ); if ( modelLinks == null ) return; applyModels( data, params.minResolution, params.applyTo, modelLinks ); // now save it SpimData2.saveXML( result.getData(), result.getXMLFileName(), result.getClusterExtension() ); } /** * @param data * @param viewIds * @return - transformation model and explanation for each viewid, also sets global variables applyTo and minResolution */ public final ApplyParameters queryParams( final SpimData data, final List< ViewId > viewIds ) { final List< Angle > angles = SpimData2.getAllAnglesSorted( data, viewIds ); final List< Channel > channels = SpimData2.getAllChannelsSorted( data, viewIds ); final List< Illumination > illums = SpimData2.getAllIlluminationsSorted( data, viewIds ); final List< TimePoint > tps = SpimData2.getAllTimePointsSorted( data, viewIds ); final boolean multipleTimePoints = tps.size() > 1; final boolean multipleChannels = channels.size() > 1; final boolean multipleIlluminations = illums.size() > 1; final boolean multipleAngles = angles.size() > 1; final GenericDialog gd = new GenericDialog( "Choose transformation model" ); gd.addChoice( "Transformation model", modelChoice, modelChoice[ defaultModel ] ); gd.addChoice( "Apply on top of", applyToChoice, applyToChoice[ defaultApplyTo ] ); if ( multipleTimePoints ) gd.addCheckbox( "Same_transformation_for_all_timepoints", defaultSameModelTimePoints ); if ( multipleChannels ) gd.addCheckbox( "Same_transformation_for_all_channels", defaultSameModelChannels ); if ( multipleIlluminations ) gd.addCheckbox( "Same_transformation_for_all_illuminations", defaultSameModelIlluminations ); if ( multipleAngles ) gd.addCheckbox( "Same_transformation_for_all_angles", defaultSameModelAngles ); gd.addMessage( "Interactive application of transformations using the BigDataViewer is available when selecting a Rigid Model.", GUIHelper.mediumstatusfont ); gd.showDialog(); if ( gd.wasCanceled() ) return null; final ApplyParameters params = new ApplyParameters(); params.model = defaultModel = gd.getNextChoiceIndex(); params.applyTo = defaultApplyTo = gd.getNextChoiceIndex(); if ( params.model == 2 ) // if rigid, ask how to define the rigid model { final GenericDialog gd2 = new GenericDialog( "Choose application for transformation model" ); gd2.addChoice( "Define as", defineAsChoice, defineAsChoice[ defaultDefineAs ] ); gd2.showDialog(); if ( gd2.wasCanceled() ) return null; params.defineAs = defaultDefineAs = gd2.getNextChoiceIndex(); } else { params.defineAs = 0; // for all others there is only matrix } if ( multipleTimePoints ) params.sameModelTimePoints = defaultSameModelTimePoints = gd.getNextBoolean(); else params.sameModelTimePoints = true; if ( multipleChannels ) params.sameModelChannels = defaultSameModelChannels = gd.getNextBoolean(); else params.sameModelChannels = true; if ( multipleIlluminations ) params.sameModelIlluminations = defaultSameModelIlluminations = gd.getNextBoolean(); else params.sameModelIlluminations = true; if ( multipleAngles ) params.sameModelAngles = defaultSameModelAngles = gd.getNextBoolean(); else params.sameModelAngles = true; // reset the transform to the calibration (x,y,z resolution), in this case we need to make sure the calibration is actually available if ( params.applyTo == 1 ) { params.minResolution = assembleAllMetaData( data.getSequenceDescription(), viewIds ); if ( Double.isNaN( params.minResolution ) ) { IOFunctions.println( "Could not assemble the calibration, quitting." ); return null; } } else { params.minResolution = 1; } return params; } public Map< ViewDescription, Pair< double[], String > > queryBigDataViewer( final SpimData data, final List< ViewId > viewIds, final ApplyParameters params ) { if ( !params.sameModelAngles || !params.sameModelChannels || !params.sameModelIlluminations || !params.sameModelIlluminations ) { IOFunctions.println( "You selected to not have the same transformation model for all views in the" ); IOFunctions.println( "previous dialog. This is not supported using the interactive setup using the" ); IOFunctions.println( "BigDataViewer. You can select single views in the xml open dialog though." ); return null; } try { final Pair< BigDataViewer, Boolean > bdvPair = BigDataViewerBoundingBox.getBDV( data, viewIds ); if ( bdvPair == null || bdvPair.getA() == null ) return null; final BigDataViewerTransformationWindow bdvw = new BigDataViewerTransformationWindow( bdvPair.getA() ); do { try { Thread.sleep( 100 ); } catch (InterruptedException e) { e.printStackTrace(); } } while ( bdvw.isRunning() ); // was locally launched? if ( bdvPair.getB() ) BigDataViewerTransformationWindow.disposeViewerWindow( bdvPair.getA() ); if ( bdvw.wasCancelled() ) return null; final HashMap< ViewDescription, Pair< double[], String > > modelLinks = new HashMap< ViewDescription, Pair< double[], String > >(); final AffineTransform3D t = bdvw.getTransform(); final double[] m = t.getRowPackedCopy(); final String d = "Rigid transform defined by BigDataViewer"; defaultModels = new ArrayList< double[] >(); defaultModels.add( m ); for ( final ViewId viewId : viewIds ) { final ViewDescription vd = data.getSequenceDescription().getViewDescription( viewId ); modelLinks.put( vd, new ValuePair< double[], String >( m, d ) ); } return modelLinks; } catch ( Exception e ) { IOFunctions.println( "Failed to run the BigDataViewer ... " ); e.printStackTrace(); return null; } } public Map< ViewDescription, Pair< double[], String > > queryRotationAxis( final SpimData data, final List< ViewId > viewIds, final ApplyParameters params ) { if ( params.model != 2 ) { IOFunctions.println( "No rigid model selected." ); return null; } // build a sparse four-dimensional table containing all entries that need to be queried // assign indices 0...n-1 to individual TimePoints, Channels, Illums, Angles // if same model for a type of entry (e.g. timepoint), than all Entries will link to the same index final HashMap< TimePoint, Integer > mapT = new HashMap< TimePoint, Integer >(); final HashMap< Channel, Integer > mapC = new HashMap< Channel, Integer >(); final HashMap< Illumination, Integer > mapI = new HashMap< Illumination, Integer >(); final HashMap< Angle, Integer > mapA = new HashMap< Angle, Integer >(); // iterate first angles, then illums, then channels, then timepoints final ListImg< ModelLink > img = createTable( data, viewIds, params.sameModelTimePoints, params.sameModelChannels, params.sameModelIlluminations, params.sameModelAngles, mapT, mapC, mapI, mapA ); final ListRandomAccess< ModelLink > ra = img.randomAccess(); final int[] l = new int[ img.numDimensions() ]; // populate the table for ( final ViewId viewId : viewIds ) { final ViewDescription vd = data.getSequenceDescription().getViewDescription( viewId ); locationForViewDescription( l, vd, mapT, mapC, mapI, mapA ); ra.setPosition( l ); if ( ra.get() == null ) ra.set( new ModelLink( vd ) ); else ra.get().add( vd ); } final int numEntries = numEntries( img ); IOFunctions.println( "Querying " + numEntries + " different rotation axis models." ); if ( defaultAxis == null || defaultDegrees == null || defaultAxis.length != numEntries || defaultDegrees.length != numEntries ) { defaultAxis = new int[ numEntries ]; defaultDegrees = new double[ numEntries ]; int j = 0; for ( final ModelLink ml : img ) if ( ml != null ) { defaultDegrees[ j ] = 0; defaultAxis[ j ] = 0; try { final Angle a = ml.viewDescriptions().get( 0 ).getViewSetup().getAngle(); final double[] axis = a.getRotationAxis(); if ( axis != null ) { if ( axis[ 0 ] == 1 && axis[ 1 ] == 0 && axis[ 2 ] == 0 ) defaultAxis[ j ] = 0; else if ( axis[ 0 ] == 0 && axis[ 1 ] == 1 && axis[ 2 ] == 0 ) defaultAxis[ j ] = 1; else if ( axis[ 0 ] == 0 && axis[ 1 ] == 0 && axis[ 2 ] == 2 ) defaultAxis[ j ] = 2; } if ( !Double.isNaN( a.getRotationAngleDegrees() ) ) defaultDegrees[ j ] = a.getRotationAngleDegrees(); else defaultDegrees[ j ] = Double.parseDouble( a.getName() ); } catch ( Exception e ) {}; ++j; } } final GenericDialog gd = new GenericDialog( "Parameters for rigid model 3d" ); int j = 0; for ( final ModelLink ml : img ) if ( ml != null ) { gd.addChoice( "Axis_" + ml.dialogName(), axesChoice, axesChoice[ defaultAxis[ j ] ] ); gd.addSlider( "Rotation_" + ml.dialogName(), -360, 360, defaultDegrees[ j ] ); ++j; } if ( numEntries > 5 ) GUIHelper.addScrollBars( gd ); gd.showDialog(); if ( gd.wasCanceled() ) return null; final HashMap< ViewDescription, Pair< double[], String > > modelLinks = new HashMap< ViewDescription, Pair< double[], String > >(); defaultModels = new ArrayList< double[] >(); final int[] axes = new int[ numEntries ]; final double[] degrees = new double[ numEntries ]; final double[] tmp = new double[ 16 ]; j = 0; for ( final ModelLink ml : img ) { if ( ml != null ) { axes[ j ] = gd.getNextChoiceIndex(); degrees[ j ] = gd.getNextNumber(); final String d; final Transform3D t = new Transform3D(); if ( axes[ j ] == 0 ) { t.rotX( Math.toRadians( degrees[ j ] ) ); d = "Rotation around x-axis by " + degrees[ j ] + " degrees"; } else if ( axes[ j ] == 1 ) { t.rotY( Math.toRadians( degrees[ j ] ) ); d = "Rotation around y-axis by " + degrees[ j ] + " degrees"; } else { t.rotZ( Math.toRadians( degrees[ j ] ) ); d = "Rotation around z-axis by " + degrees[ j ] + " degrees"; } t.get( tmp ); final double[] m = new double[]{ tmp[ 0 ], tmp[ 1 ], tmp[ 2 ], tmp[ 3 ], tmp[ 4 ], tmp[ 5 ], tmp[ 6 ], tmp[ 7 ], tmp[ 8 ], tmp[ 9 ], tmp[ 10 ], tmp[ 11 ] }; defaultModels.add( m ); ml.setModel( m, d ); for ( final ViewDescription vd : ml.viewDescriptions() ) modelLinks.put( vd, new ValuePair< double[], String >( ml.model(), ml.modelDescription() ) ); } } // set defaults defaultAxis = axes; defaultDegrees = degrees; return modelLinks; } public Map< ViewDescription, Pair< double[], String > > queryString( final SpimData data, final List< ViewId > viewIds, final ApplyParameters params ) { // build a sparse four-dimensional table containing all entries that need to be queried // assign indices 0...n-1 to individual TimePoints, Channels, Illums, Angles // if same model for a type of entry (e.g. timepoint), than all Entries will link to the same index final HashMap< TimePoint, Integer > mapT = new HashMap< TimePoint, Integer >(); final HashMap< Channel, Integer > mapC = new HashMap< Channel, Integer >(); final HashMap< Illumination, Integer > mapI = new HashMap< Illumination, Integer >(); final HashMap< Angle, Integer > mapA = new HashMap< Angle, Integer >(); // iterate first angles, then illums, then channels, then timepoints final ListImg< ModelLink > img = createTable( data, viewIds, params.sameModelTimePoints, params.sameModelChannels, params.sameModelIlluminations, params.sameModelAngles, mapT, mapC, mapI, mapA ); final ListRandomAccess< ModelLink > ra = img.randomAccess(); final int[] l = new int[ img.numDimensions() ]; // populate the table for ( final ViewId viewId : viewIds ) { final ViewDescription vd = data.getSequenceDescription().getViewDescription( viewId ); locationForViewDescription( l, vd, mapT, mapC, mapI, mapA ); ra.setPosition( l ); if ( ra.get() == null ) ra.set( new ModelLink( vd ) ); else ra.get().add( vd ); } final int numEntries = numEntries( img ); IOFunctions.println( "Querying " + numEntries + " different transformation models." ); if ( defaultModels == null || defaultModels.size() == 0 || defaultModels.size() != numEntries ) { defaultModels = new ArrayList< double[] >(); for ( int i = 0; i < numEntries; ++i ) defaultModels.add( new double[]{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0 } ); } final GenericDialog gd; if ( params.model == 0 ) // identity transform { gd = null; } else if ( params.model == 1 ) // translation model { gd = new GenericDialog( "Model parameters for translation model 3d" ); gd.addMessage( "t(x) = m03, t(y) = m13, t(z) = m23" ); gd.addMessage( "" ); gd.addMessage( "Please provide 3d translation in this form (any brackets will be ignored): m03, m13, m23" ); int j = 0; for ( final ModelLink ml : img ) if ( ml != null ) gd.addStringField( ml.dialogName(), defaultModels.get( j )[ 3 ] + ", " + defaultModels.get( j )[ 7 ] + ", " + defaultModels.get( j++ )[ 11 ], 30 ); } else // rigid/affine model { gd = new GenericDialog( "Model parameters for rigid/affine model 3d" ); gd.addMessage( "| m00 m01 m02 m03 |\n" + "| m10 m11 m12 m13 |\n" + "| m20 m21 m22 m23 |" ); gd.addMessage( "Please provide 3d rigid/affine in this form (any brackets will be ignored):\n" + "m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23" ); int j = 0; for ( final ModelLink ml : img ) if ( ml != null ) { final double[] m = defaultModels.get( j++ ); gd.addStringField( ml.dialogName(), m[ 0 ] + ", " + m[ 1 ] + ", " + m[ 2 ] + ", " + m[ 3 ] + ", " + m[ 4 ] + ", " + m[ 5 ] + ", " + m[ 6 ] + ", " + m[ 7 ] + ", " + m[ 8 ] + ", " + m[ 9 ] + ", " + m[ 10 ] + ", " + m[ 11 ], 80 ); } } if ( gd != null ) { if ( numEntries > 10 ) GUIHelper.addScrollBars( gd ); gd.showDialog(); if ( gd.wasCanceled() ) return null; } final HashMap< ViewDescription, Pair< double[], String > > modelLinks = new HashMap< ViewDescription, Pair< double[], String > >(); defaultModels = new ArrayList< double[] >(); for ( final ModelLink ml : img ) { if ( ml != null ) { final double[] m; final String d; if ( params.model == 0 ) { m = null; d = "Identity"; } else if ( params.model == 1 ) { final double[] v = parseString( gd.getNextString(), 3 ); if ( v == null ) return null; else { m = new double[]{ 1, 0, 0, v[ 0 ], 0, 1, 0, v[ 1 ], 0, 0, 1, v[ 2 ] }; d = "Translation [" + v[ 0 ] + "," + v[ 1 ] + "," + v[ 2 ] + "]"; } } else { m = parseString( gd.getNextString(), 12 ); if ( m == null ) return null; else d = "Rigid/Affine by matrix"; } if ( m == null ) defaultModels.add( new double[]{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0 } ); else defaultModels.add( m ); ml.setModel( m, d ); for ( final ViewDescription vd : ml.viewDescriptions() ) modelLinks.put( vd, new ValuePair< double[], String >( ml.model(), ml.modelDescription() ) ); } } return modelLinks; } public void applyModels( final SpimData spimData, final double minResolution, final int applyTo, final Map< ViewDescription, Pair< double[], String > > modelLinks ) { for ( final ViewDescription vd : modelLinks.keySet() ) { final double[] v = modelLinks.get( vd ).getA(); final String modelDesc = modelLinks.get( vd ).getB(); final TimePoint t = vd.getTimePoint(); final Channel c = vd.getViewSetup().getChannel(); final Illumination i = vd.getViewSetup().getIllumination(); final Angle a = vd.getViewSetup().getAngle(); if ( applyTo == 0 ) { IOFunctions.println( "Reseting model to identity transform for timepoint " + t.getName() + ", channel " + c.getName() + ", illum " + i.getName() + ", angle " + a.getName() ); setModelToIdentity( spimData, vd ); } else if ( applyTo == 1 ) { IOFunctions.println( "Reseting model to calibration for timepoint " + t.getName() + ", channel " + c.getName() + ", illum " + i.getName() + ", angle " + a.getName() ); setModelToCalibration( spimData, vd, minResolution ); } if ( v != null ) { IOFunctions.println( "Applying model " + Util.printCoordinates( v ) + " (" + modelDesc + ") to timepoint " + t.getName() + ", channel " + c.getName() + ", illum " + i.getName() + ", angle " + a.getName() ); final AffineTransform3D model = new AffineTransform3D(); model.set( v ); preConcatenateTransform( spimData, vd, model, "Manually defined transformation (" + modelDesc + ")" ); } } } private static final void locationForViewDescription( final int[] l, final ViewDescription vd, final HashMap< TimePoint, Integer > mapT, final HashMap< Channel, Integer > mapC, final HashMap< Illumination, Integer > mapI, final HashMap< Angle, Integer > mapA ) { final TimePoint t = vd.getTimePoint(); final Channel c = vd.getViewSetup().getChannel(); final Illumination i = vd.getViewSetup().getIllumination(); final Angle a = vd.getViewSetup().getAngle(); l[ 0 ] = mapA.get( a ); l[ 1 ] = mapI.get( i ); l[ 2 ] = mapC.get( c ); l[ 3 ] = mapT.get( t ); } private static final int numEntries( final ListImg< ModelLink > img ) { int numEntries = 0; for ( final ModelLink l : img ) if ( l != null ) ++numEntries; return numEntries; } private static final ListImg< ModelLink > createTable( final SpimData data, final List< ViewId > viewIds, final boolean sameModelTimePoints, final boolean sameModelChannels, final boolean sameModelIlluminations, final boolean sameModelAngles, final HashMap< TimePoint, Integer > mapT, final HashMap< Channel, Integer > mapC, final HashMap< Illumination, Integer > mapI, final HashMap< Angle, Integer > mapA ) { final List< TimePoint > ts = SpimData2.getAllTimePointsSorted( data, viewIds ); final List< Channel > cs = SpimData2.getAllChannelsSorted( data, viewIds ); final List< Illumination > is = SpimData2.getAllIlluminationsSorted( data, viewIds ); final List< Angle > as = SpimData2.getAllAnglesSorted( data, viewIds ); final int nT = sameModelTimePoints ? 1 : ts.size(); for ( int i = 0; i < ts.size(); ++i ) mapT.put( ts.get( i ), sameModelTimePoints ? 0 : i ); final int nC = sameModelChannels ? 1 : cs.size(); for ( int i = 0; i < cs.size(); ++i ) mapC.put( cs.get( i ), sameModelChannels ? 0 : i ); final int nI = sameModelIlluminations ? 1 : is.size(); for ( int i = 0; i < is.size(); ++i ) mapI.put( is.get( i ), sameModelIlluminations ? 0 : i ); final int nA = sameModelAngles ? 1 : as.size(); for ( int i = 0; i < as.size(); ++i ) mapA.put( as.get( i ), sameModelAngles ? 0 : i ); // iterate first angles, then illums, then channels, then timepoints return new ListImgFactory< ModelLink >().create( new long[]{ nA, nI, nC, nT }, new ModelLink( null ) ); } protected static final double[] parseString( String entry, final int numValues ) { while ( entry.contains( "(" ) ) entry.replace( "(", "" ); while ( entry.contains( ")" ) ) entry.replace( ")", "" ); while ( entry.contains( "[" ) ) entry.replace( "[", "" ); while ( entry.contains( "]" ) ) entry.replace( "]", "" ); entry = entry.replaceAll( " ", "" ).trim(); String[] entries = entry.split( "," ); if ( entries.length != numValues ) { IOFunctions.println( "Cannot parse: '" + entry + "', has " + entries.length + " numbers, but should be " + numValues ); return null; } final double[] v = new double[ numValues ]; for ( int j = 0; j < numValues; ++j ) v[ j ] = Double.parseDouble( entries[ j ].trim() ); return v; } public static void preConcatenateTransform( final SpimData spimData, final ViewId viewId, final AffineTransform3D model, final String name ) { final ViewRegistrations viewRegistrations = spimData.getViewRegistrations(); // update the view registration final ViewRegistration vr = viewRegistrations.getViewRegistration( viewId ); final ViewTransform vt = new ViewTransformAffine( name, model ); vr.preconcatenateTransform( vt ); } public static void setModelToCalibration( final SpimData spimData, final ViewId viewId, final double minResolution ) { setModelToIdentity( spimData, viewId ); final ViewRegistrations viewRegistrations = spimData.getViewRegistrations(); final ViewRegistration r = viewRegistrations.getViewRegistration( viewId ); final ViewDescription viewDescription = spimData.getSequenceDescription().getViewDescription( viewId.getTimePointId(), viewId.getViewSetupId() ); VoxelDimensions voxelSize = ViewSetupUtils.getVoxelSizeOrLoad( viewDescription.getViewSetup(), viewDescription.getTimePoint(), spimData.getSequenceDescription().getImgLoader() ); final double calX = voxelSize.dimension( 0 ) / minResolution; final double calY = voxelSize.dimension( 1 ) / minResolution; final double calZ = voxelSize.dimension( 2 ) / minResolution; final AffineTransform3D m = new AffineTransform3D(); m.set( calX, 0.0f, 0.0f, 0.0f, 0.0f, calY, 0.0f, 0.0f, 0.0f, 0.0f, calZ, 0.0f ); final ViewTransform vt = new ViewTransformAffine( "calibration", m ); r.preconcatenateTransform( vt ); } public static void setModelToIdentity( final SpimData spimData, final ViewId viewId ) { final ViewRegistrations viewRegistrations = spimData.getViewRegistrations(); final ViewRegistration r = viewRegistrations.getViewRegistration( viewId ); r.identity(); } /** * Should be called before registration to make sure all metadata is right * * @return - minimal resolution in all dimensions */ public static double assembleAllMetaData( final SequenceDescription sequenceDescription, final Collection< ? extends ViewId > viewIdsToProcess ) { double minResolution = Double.MAX_VALUE; for ( final ViewId viewId : viewIdsToProcess ) { final ViewDescription vd = sequenceDescription.getViewDescription( viewId.getTimePointId(), viewId.getViewSetupId() ); if ( !vd.isPresent() ) continue; ViewSetup setup = vd.getViewSetup(); // load metadata to update the registrations if required // only use calibration as defined in the metadata if ( !setup.hasVoxelSize() ) { VoxelDimensions voxelSize = sequenceDescription.getImgLoader().getSetupImgLoader( viewId.getViewSetupId() ).getVoxelSize( viewId.getTimePointId() ); if ( voxelSize == null ) { IOFunctions.println( "An error occured. Cannot load calibration for" + " timepoint: " + vd.getTimePoint().getName() + " angle: " + vd.getViewSetup().getAngle().getName() + " channel: " + vd.getViewSetup().getChannel().getName() + " illum: " + vd.getViewSetup().getIllumination().getName() ); IOFunctions.println( "Quitting. Please set it manually when defining the dataset or by modifying the XML" ); return Double.NaN; } setup.setVoxelSize( voxelSize ); } if ( !setup.hasVoxelSize() ) { IOFunctions.println( "An error occured. No calibration available for" + " timepoint: " + vd.getTimePoint().getName() + " angle: " + vd.getViewSetup().getAngle().getName() + " channel: " + vd.getViewSetup().getChannel().getName() + " illum: " + vd.getViewSetup().getIllumination().getName() ); IOFunctions.println( "Quitting. Please set it manually when defining the dataset or by modifying the XML." ); IOFunctions.println( "Note: if you selected to load calibration independently for each image, it should." ); IOFunctions.println( " have been loaded during interest point detection." ); return Double.NaN; } VoxelDimensions voxelSize = setup.getVoxelSize(); final double calX = voxelSize.dimension( 0 ); final double calY = voxelSize.dimension( 1 ); final double calZ = voxelSize.dimension( 2 ); minResolution = Math.min( minResolution, calX ); minResolution = Math.min( minResolution, calY ); minResolution = Math.min( minResolution, calZ ); } return minResolution; } public static void applyAxis( final SpimData data ) { ViewRegistrations viewRegistrations = data.getViewRegistrations(); for ( final ViewDescription vd : data.getSequenceDescription().getViewDescriptions().values() ) { if ( vd.isPresent() ) { final Angle a = vd.getViewSetup().getAngle(); if ( a.hasRotation() ) { final ViewRegistration vr = viewRegistrations.getViewRegistration( vd ); final Dimensions dim = vd.getViewSetup().getSize(); AffineTransform3D model = new AffineTransform3D(); model.set( 1, 0, 0, -dim.dimension( 0 )/2, 0, 1, 0, -dim.dimension( 1 )/2, 0, 0, 1, -dim.dimension( 2 )/2 ); ViewTransform vt = new ViewTransformAffine( "Center view", model ); vr.preconcatenateTransform( vt ); final double[] tmp = new double[ 16 ]; final double[] axis = a.getRotationAxis(); final double degrees = a.getRotationAngleDegrees(); final Transform3D t = new Transform3D(); final String d; if ( axis[ 0 ] == 1 && axis[ 1 ] == 0 && axis[ 2 ] == 0 ) { t.rotX( Math.toRadians( degrees ) ); d = "Rotation around x-axis by " + degrees + " degrees"; } else if ( axis[ 0 ] == 0 && axis[ 1 ] == 1 && axis[ 2 ] == 0 ) { t.rotY( Math.toRadians( degrees ) ); d = "Rotation around y-axis by " + degrees + " degrees"; } else if ( axis[ 0 ] == 1 && axis[ 0 ] == 0 && axis[ 2 ] == 1 ) { t.rotZ( Math.toRadians( degrees ) ); d = "Rotation around z-axis by " + degrees + " degrees"; } else { IOFunctions.println( "Arbitrary rotation axis not supported yet." ); continue; } t.get( tmp ); model = new AffineTransform3D(); model.set( tmp[ 0 ], tmp[ 1 ], tmp[ 2 ], tmp[ 3 ], tmp[ 4 ], tmp[ 5 ], tmp[ 6 ], tmp[ 7 ], tmp[ 8 ], tmp[ 9 ], tmp[ 10 ], tmp[ 11 ] ); vt = new ViewTransformAffine( d, model ); vr.preconcatenateTransform( vt ); vr.updateModel(); } } } } /** * @param args */ public static void main( final String[] args ) { //new BigDataViewerTransformationWindow( null ); new Apply_Transformation().run( null ); } }