/*-
* #%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.ImageJ;
import ij.gui.GenericDialog;
import ij.plugin.PlugIn;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import mpicbg.spim.data.registration.ViewRegistration;
import mpicbg.spim.data.registration.ViewRegistrations;
import mpicbg.spim.data.registration.ViewTransform;
import mpicbg.spim.data.sequence.Angle;
import mpicbg.spim.data.sequence.Channel;
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 mpicbg.spim.io.IOFunctions;
import spim.fiji.plugin.queryXML.GenericLoadParseQueryXML;
import spim.fiji.plugin.queryXML.LoadParseQueryXML;
import spim.fiji.spimdata.SpimData2;
public class Duplicate_Transformation implements PlugIn
{
public static String[] duplicationChoice = new String[]{
"One timepoint to other timepoints",
"One channel to other channels",
"One illumination direction to other illumination directions",
"One angle to other angles",
};
public static String[] transformationChoice = new String[]{
"Replace all transformations",
"Add last transformation only",
"Add multiple transformations"
};
public static String[] tpChoice = new String[]{ "All Timepoints", "Single Timepoint (Select from List)", "Multiple Timepoints (Select from List)", "Range of Timepoints (Specify by Name)" };
public static int defaultTPChoice = 0;
public static int defaultTimePointIndex = 0;
public static boolean[] defaultTimePointIndices = null;
public static String defaultTimePointString = null;
public static String[] angleChoice = new String[]{ "All Angles", "Single Angle (Select from List)", "Multiple Angles (Select from List)", "Range of Angles (Specify by Name)" };
public static int defaultAngleChoice = 0;
public static int defaultAngleIndex = 0;
public static boolean[] defaultAngleIndices = null;
public static String defaultAngleString = null;
public static String[] channelChoice = new String[]{ "All Channels", "Single Channel (Select from List)", "Multiple Channels (Select from List)", "Range of Channels (Specify by Name)" };
public static int defaultChannelChoice = 0;
public static int defaultChannelIndex = 0;
public static boolean[] defaultChannelIndices = null;
public static String defaultChannelString = null;
public static String[] illumChoice = new String[]{ "All Illumination Directions", "Single Illumination Directions (Select from List)", "Multiple Illumination Directions (Select from List)", "Range of Illumination Directions (Specify by Name)" };
public static int defaultIllumChoice = 0;
public static int defaultIllumIndex = 0;
public static boolean[] defaultIllumIndices = null;
public static String defaultIllumString = null;
public static int defaultChoice = 0;
public static int defaultTransformationChoice = 0;
public static int defaultNumTransformations = 2;
public static int defaultTimePoint = 0;
public static int defaultSelectedTimePointIndex = 1;
public static int defaultChannel = 0;
public static int defaultSelectedChannelIndex = 1;
public static int defaultIllum = 0;
public static int defaultSelectedIllumIndex = 1;
public static int defaultAngle = 0;
public static int defaultSelectedAngleIndex = 1;
@Override
public void run( final String arg0 )
{
final GenericDialog gd = new GenericDialog( "Define Duplication" );
gd.addChoice( "Apply transformation of", duplicationChoice, duplicationChoice[ defaultChoice ] );
gd.showDialog();
if ( gd.wasCanceled() )
return;
final int choice = defaultChoice = gd.getNextChoiceIndex();
final boolean askForTimepoints = choice != 0;
final boolean askForChannels = choice != 1;
final boolean askForIllum = choice != 2;
final boolean askForAngles = choice != 3;
final LoadParseQueryXML result = new LoadParseQueryXML();
if ( !result.queryXML( "duplicating transformations", "Apply to", askForAngles, askForChannels, askForIllum, askForTimepoints ) )
return;
if ( !askForTimepoints )
{
if ( result.getTimePointsToProcess().size() == 1 )
{
IOFunctions.println( "Only one timepoint available, cannot apply to another timepoint." );
return;
}
else
{
if ( !applyTimepoints( result ) )
return;
}
}
else if ( !askForChannels )
{
if ( result.getChannelsToProcess().size() == 1 )
{
IOFunctions.println( "Only one channel available, cannot apply to another channel." );
return;
}
else
{
if ( !applyChannels( result ) )
return;
}
}
else if ( !askForIllum )
{
if ( result.getIlluminationsToProcess().size() == 1 )
{
IOFunctions.println( "Only one illumination direction available, cannot apply to another illumination direction." );
return;
}
else
{
if ( !applyIllums( result ) )
return;
}
}
else if ( !askForAngles )
{
if ( result.getAnglesToProcess().size() == 1 )
{
IOFunctions.println( "Only one angle available, cannot apply to another angle." );
return;
}
else
{
if ( !applyAngles( result ) )
return;
}
}
// now save it in case something was applied
SpimData2.saveXML( result.getData(), new File( result.getXMLFileName() ).getName(), result.getClusterExtension() );
}
protected void askForRegistrations( final GenericDialog gd )
{
gd.addMessage( "" );
gd.addChoice( "Duplicate_which_transformations", transformationChoice, transformationChoice[ defaultTransformationChoice ] );
}
/**
*
* @param gd
* @return -1 means invalid/cancelled, 0 means all, >0 means how many
*/
protected int parseRegistrations( final GenericDialog gd )
{
int transformation = defaultTransformationChoice = gd.getNextChoiceIndex();
if ( transformation == 2 )
{
final GenericDialog gd2 = new GenericDialog( "Choose number of transformations" );
gd2.addNumericField( "Number of transformations to add", defaultNumTransformations, 0 );
gd2.showDialog();
if ( gd2.wasCanceled() )
return -1;
else
transformation = (int)Math.round( gd2.getNextNumber() );
}
return transformation;
}
protected void duplicateTransformations( final int transformations, final ViewId sourceViewId, final ViewId targetViewId, final SpimData2 spimData )
{
final ViewDescription sourceVD = spimData.getSequenceDescription().getViewDescription(
sourceViewId.getTimePointId(), sourceViewId.getViewSetupId() );
final ViewDescription targetVD = spimData.getSequenceDescription().getViewDescription(
targetViewId.getTimePointId(), targetViewId.getViewSetupId() );
final ViewSetup sourceVS = sourceVD.getViewSetup();
final ViewSetup targetVS = targetVD.getViewSetup();
IOFunctions.println( "Source viewId t=" + sourceVD.getTimePoint().getName() + ", ch=" + sourceVS.getChannel().getName() + ", ill=" + sourceVS.getIllumination().getName() + ", angle=" + sourceVS.getAngle().getName() );
IOFunctions.println( "Target viewId t=" + targetVD.getTimePoint().getName() + ", ch=" + targetVS.getChannel().getName() + ", ill=" + targetVS.getIllumination().getName() + ", angle=" + targetVS.getAngle().getName() );
if ( !sourceVD.isPresent() || !targetVD.isPresent() )
{
if ( !sourceVD.isPresent() )
IOFunctions.println( "Source viewId is NOT present" );
if ( !targetVD.isPresent() )
IOFunctions.println( "Target viewId is NOT present" );
return;
}
// update the view registration
final ViewRegistrations viewRegistrations = spimData.getViewRegistrations();
final ViewRegistration vrSource = viewRegistrations.getViewRegistration( sourceViewId );
final ViewRegistration vrTarget = viewRegistrations.getViewRegistration( targetViewId );
// reset the transformation and add all
if ( transformations == 0 )
{
vrTarget.identity();
for ( final ViewTransform vt : vrSource.getTransformList() )
{
IOFunctions.println( "Concatenationg model " + vt.getName() + ", " + vt.asAffine3D() );
vrTarget.concatenateTransform( vt );
}
}
else
{
// copy the last n transformations
final ArrayList< ViewTransform > vts = new ArrayList< ViewTransform >();
for ( int k = 0; k < transformations; ++k )
vts.add( vrSource.getTransformList().get( k ) );
// and add them at the end
for ( int k = vts.size() - 1; k >= 0; --k )
{
final ViewTransform vt = vts.get( k );
IOFunctions.println( "Adding model " + vt.getName() + ", " + vt.asAffine3D() );
vrTarget.preconcatenateTransform( vt );
}
}
}
protected boolean applyTimepoints( final LoadParseQueryXML result )
{
final GenericDialog gd = new GenericDialog( "Define source and target timepoints" );
final String[] timepoints = assembleTimepoints( result.getTimePointsToProcess() );
if ( defaultTimePoint >= timepoints.length )
defaultTimePoint = 0;
gd.addChoice( "Source timepoint", timepoints, timepoints[ defaultTimePoint ] );
gd.addChoice( "Target timepoint(s)", tpChoice, tpChoice[ defaultTPChoice ] );
askForRegistrations( gd );
gd.showDialog();
if ( gd.wasCanceled() )
return false;
final TimePoint source = result.getTimePointsToProcess().get( defaultTimePoint = gd.getNextChoiceIndex() );
final ArrayList< TimePoint > targets = new ArrayList< TimePoint >();
final int choice = defaultTPChoice = gd.getNextChoiceIndex();
if ( choice == 1 )
{
if ( defaultSelectedTimePointIndex >= timepoints.length )
defaultSelectedTimePointIndex = 1;
final int selection = GenericLoadParseQueryXML.queryIndividualEntry( "Timepoint", timepoints, defaultSelectedTimePointIndex );
if ( selection >= 0 )
targets.add( result.getTimePointsToProcess().get( defaultSelectedTimePointIndex = selection ) );
else
return false;
}
else if ( choice == 2 || choice == 3 ) // choose multiple timepoints or timepoints defined by pattern
{
final boolean[] selection;
String[] defaultTimePoint = new String[]{ defaultTimePointString };
if ( choice == 2 )
selection = GenericLoadParseQueryXML.queryMultipleEntries( "Timepoints", timepoints, defaultTimePointIndices );
else
selection = GenericLoadParseQueryXML.queryPattern( "Timepoints", timepoints, defaultTimePoint );
if ( selection == null )
return false;
else
{
defaultTimePointIndices = selection;
if ( choice == 3 )
defaultTimePointString = defaultTimePoint[ 0 ];
for ( int i = 0; i < selection.length; ++i )
if ( selection[ i ] )
targets.add( result.getTimePointsToProcess().get( i ) );
}
}
else
{
targets.addAll( result.getTimePointsToProcess() );
}
if ( targets.size() == 0 )
{
IOFunctions.println( "List of timepoints is empty. Stopping." );
return false;
}
else
{
final int transformations = parseRegistrations( gd );
if ( transformations < 0 )
return false;
int countApplied = 0;
for ( int j = 0; j < targets.size(); ++j )
if ( !source.equals( targets.get( j ) ) )
{
IOFunctions.println( "Applying timepoint " + source.getName() + " >>> " + targets.get( j ).getName() );
++countApplied;
for ( final Channel c : result.getChannelsToProcess() )
for ( final Illumination i : result.getIlluminationsToProcess() )
for ( final Angle a : result.getAnglesToProcess() )
{
final ViewId sourceViewId = SpimData2.getViewId( result.getData().getSequenceDescription(), source, c, a, i );
final ViewId targetViewId = SpimData2.getViewId( result.getData().getSequenceDescription(), targets.get( j ), c, a, i );
// this happens only if a viewsetup is not present in any timepoint
// (e.g. after appending fusion to a dataset)
if ( sourceViewId == null || targetViewId == null )
continue;
duplicateTransformations( transformations, sourceViewId, targetViewId, result.getData() );
}
}
if ( countApplied == 0 )
return false;
}
return true;
}
protected boolean applyChannels( final LoadParseQueryXML result )
{
final GenericDialog gd = new GenericDialog( "Define source and target channels" );
final String[] channels = assembleChannels( result.getChannelsToProcess() );
if ( defaultChannel >= channels.length )
defaultChannel = 0;
gd.addChoice( "Source channel", channels, channels[ defaultChannel ] );
gd.addChoice( "Target channel(s)", channelChoice, channelChoice[ defaultChannelChoice ] );
askForRegistrations( gd );
gd.showDialog();
if ( gd.wasCanceled() )
return false;
final Channel source = result.getChannelsToProcess().get( defaultChannel = gd.getNextChoiceIndex() );
final ArrayList< Channel > targets = new ArrayList< Channel >();
final int choice = defaultChannelChoice = gd.getNextChoiceIndex();
if ( choice == 1 )
{
if ( defaultSelectedChannelIndex >= channels.length )
defaultSelectedChannelIndex = 1;
final int selection = GenericLoadParseQueryXML.queryIndividualEntry( "Channel", channels, defaultSelectedChannelIndex );
if ( selection >= 0 )
targets.add( result.getChannelsToProcess().get( defaultSelectedChannelIndex = selection ) );
else
return false;
}
else if ( choice == 2 || choice == 3 ) // choose multiple channels or channels defined by pattern
{
final boolean[] selection;
String[] defaultChannel = new String[]{ defaultChannelString };
if ( choice == 2 )
selection = GenericLoadParseQueryXML.queryMultipleEntries( "Channels", channels, defaultChannelIndices );
else
selection = GenericLoadParseQueryXML.queryPattern( "Channels", channels, defaultChannel );
if ( selection == null )
return false;
else
{
defaultChannelIndices = selection;
if ( choice == 3 )
defaultChannelString = defaultChannel[ 0 ];
for ( int i = 0; i < selection.length; ++i )
if ( selection[ i ] )
targets.add( result.getChannelsToProcess().get( i ) );
}
}
else
{
targets.addAll( result.getChannelsToProcess() );
}
if ( targets.size() == 0 )
{
IOFunctions.println( "List of channels is empty. Stopping." );
return false;
}
else
{
final int transformations = parseRegistrations( gd );
if ( transformations < 0 )
return false;
int countApplied = 0;
for ( int j = 0; j < targets.size(); ++j )
if ( !source.equals( targets.get( j ) ) )
{
IOFunctions.println( "Applying chanel " + source.getName() + " >>> " + targets.get( j ).getName() );
++countApplied;
for ( final TimePoint t : result.getTimePointsToProcess() )
for ( final Illumination i : result.getIlluminationsToProcess() )
for ( final Angle a : result.getAnglesToProcess() )
{
final ViewId sourceViewId = SpimData2.getViewId( result.getData().getSequenceDescription(), t, source, a, i );
final ViewId targetViewId = SpimData2.getViewId( result.getData().getSequenceDescription(), t, targets.get( j ), a, i );
// this happens only if a viewsetup is not present in any timepoint
// (e.g. after appending fusion to a dataset)
if ( sourceViewId == null || targetViewId == null )
continue;
duplicateTransformations( transformations, sourceViewId, targetViewId, result.getData() );
}
}
if ( countApplied == 0 )
return false;
}
return true;
}
protected boolean applyIllums( final LoadParseQueryXML result )
{
final GenericDialog gd = new GenericDialog( "Define source and target illumination directions" );
final String[] illums = assembleIllums( result.getIlluminationsToProcess() );
if ( defaultIllum >= illums.length )
defaultIllum = 0;
gd.addChoice( "Source illumination direction", illums, illums[ defaultIllum ] );
gd.addChoice( "Target illumination direction(s)", illumChoice, illumChoice[ defaultIllumChoice ] );
askForRegistrations( gd );
gd.showDialog();
if ( gd.wasCanceled() )
return false;
final Illumination source = result.getIlluminationsToProcess().get( defaultIllum = gd.getNextChoiceIndex() );
final ArrayList< Illumination > targets = new ArrayList< Illumination >();
final int choice = defaultIllumChoice = gd.getNextChoiceIndex();
if ( choice == 1 )
{
if ( defaultSelectedIllumIndex >= illums.length )
defaultSelectedIllumIndex = 1;
final int selection = GenericLoadParseQueryXML.queryIndividualEntry( "Illumination direction", illums, defaultSelectedIllumIndex );
if ( selection >= 0 )
targets.add( result.getIlluminationsToProcess().get( defaultSelectedIllumIndex = selection ) );
else
return false;
}
else if ( choice == 2 || choice == 3 ) // choose multiple illum dir or illum dirs defined by pattern
{
final boolean[] selection;
String[] defaultIllum = new String[]{ defaultIllumString };
if ( choice == 2 )
selection = GenericLoadParseQueryXML.queryMultipleEntries( "Illumination directions", illums, defaultIllumIndices );
else
selection = GenericLoadParseQueryXML.queryPattern( "Illumination directions", illums, defaultIllum );
if ( selection == null )
return false;
else
{
defaultIllumIndices = selection;
if ( choice == 3 )
defaultIllumString = defaultIllum[ 0 ];
for ( int i = 0; i < selection.length; ++i )
if ( selection[ i ] )
targets.add( result.getIlluminationsToProcess().get( i ) );
}
}
else
{
targets.addAll( result.getIlluminationsToProcess() );
}
if ( targets.size() == 0 )
{
IOFunctions.println( "List of illumination directions is empty. Stopping." );
return false;
}
else
{
final int transformations = parseRegistrations( gd );
if ( transformations < 0 )
return false;
int countApplied = 0;
for ( int j = 0; j < targets.size(); ++j )
if ( !source.equals( targets.get( j ) ) )
{
IOFunctions.println( "Applying illumination direction " + source.getName() + " >>> " + targets.get( j ).getName() );
++countApplied;
for ( final TimePoint t : result.getTimePointsToProcess() )
for ( final Channel c : result.getChannelsToProcess() )
for ( final Angle a : result.getAnglesToProcess() )
{
final ViewId sourceViewId = SpimData2.getViewId( result.getData().getSequenceDescription(), t, c, a, source );
final ViewId targetViewId = SpimData2.getViewId( result.getData().getSequenceDescription(), t, c, a, targets.get( j ) );
// this happens only if a viewsetup is not present in any timepoint
// (e.g. after appending fusion to a dataset)
if ( sourceViewId == null || targetViewId == null )
continue;
duplicateTransformations( transformations, sourceViewId, targetViewId, result.getData() );
}
}
if ( countApplied == 0 )
return false;
}
return true;
}
protected boolean applyAngles( final LoadParseQueryXML result )
{
final GenericDialog gd = new GenericDialog( "Define source and target angles" );
final String[] angles = assembleAngles( result.getAnglesToProcess() );
if ( defaultAngle >= angles.length )
defaultAngle = 0;
gd.addChoice( "Source angle", angles, angles[ defaultAngle ] );
gd.addChoice( "Target angles(s)", angleChoice, angleChoice[ defaultAngleChoice ] );
askForRegistrations( gd );
gd.showDialog();
if ( gd.wasCanceled() )
return false;
final Angle source = result.getAnglesToProcess().get( defaultAngle = gd.getNextChoiceIndex() );
final ArrayList< Angle > targets = new ArrayList< Angle >();
final int choice = defaultAngleChoice = gd.getNextChoiceIndex();
if ( choice == 1 )
{
if ( defaultSelectedAngleIndex >= angles.length )
defaultSelectedAngleIndex = 1;
final int selection = GenericLoadParseQueryXML.queryIndividualEntry( "Angle", angles, defaultSelectedAngleIndex );
if ( selection >= 0 )
targets.add( result.getAnglesToProcess().get( defaultSelectedAngleIndex = selection ) );
else
return false;
}
else if ( choice == 2 || choice == 3 ) // choose multiple angle or angles defined by pattern
{
final boolean[] selection;
String[] defaultAngle = new String[]{ defaultAngleString };
if ( choice == 2 )
selection = GenericLoadParseQueryXML.queryMultipleEntries( "Angles", angles, defaultAngleIndices );
else
selection = GenericLoadParseQueryXML.queryPattern( "Angles", angles, defaultAngle );
if ( selection == null )
return false;
else
{
defaultAngleIndices = selection;
if ( choice == 3 )
defaultAngleString = defaultAngle[ 0 ];
for ( int i = 0; i < selection.length; ++i )
if ( selection[ i ] )
targets.add( result.getAnglesToProcess().get( i ) );
}
}
else
{
targets.addAll( result.getAnglesToProcess() );
}
if ( targets.size() == 0 )
{
IOFunctions.println( "List of angles is empty. Stopping." );
return false;
}
else
{
final int transformations = parseRegistrations( gd );
if ( transformations < 0 )
return false;
int countApplied = 0;
for ( int j = 0; j < targets.size(); ++j )
if ( !source.equals( targets.get( j ) ) )
{
IOFunctions.println( "Applying angle " + source.getName() + " >>> " + targets.get( j ).getName() );
++countApplied;
for ( final TimePoint t : result.getTimePointsToProcess() )
for ( final Channel c : result.getChannelsToProcess() )
for ( final Illumination i : result.getIlluminationsToProcess() )
{
final ViewId sourceViewId = SpimData2.getViewId( result.getData().getSequenceDescription(), t, c, source, i );
final ViewId targetViewId = SpimData2.getViewId( result.getData().getSequenceDescription(), t, c, targets.get( j ), i );
// this happens only if a viewsetup is not present in any timepoint
// (e.g. after appending fusion to a dataset)
if ( sourceViewId == null || targetViewId == null )
continue;
duplicateTransformations( transformations, sourceViewId, targetViewId, result.getData() );
}
}
if ( countApplied == 0 )
return false;
}
return true;
}
protected String[] assembleTimepoints( final List< TimePoint > timepoints )
{
final String[] tps = new String[ timepoints.size() ];
for ( int t = 0; t < tps.length; ++t )
tps[ t ] = timepoints.get( t ).getName();
return tps;
}
protected String[] assembleChannels( final List< Channel > channels )
{
final String[] chs = new String[ channels.size() ];
for ( int t = 0; t < chs.length; ++t )
chs[ t ] = channels.get( t ).getName();
return chs;
}
protected String[] assembleIllums( final List< Illumination > illums )
{
final String[] is = new String[ illums.size() ];
for ( int t = 0; t < is.length; ++t )
is[ t ] = illums.get( t ).getName();
return is;
}
protected String[] assembleAngles( final List< Angle > angles )
{
final String[] as = new String[ angles.size() ];
for ( int t = 0; t < as.length; ++t )
as[ t ] = angles.get( t ).getName();
return as;
}
public static void main( final String[] args )
{
new ImageJ();
new Duplicate_Transformation().run( null );
}
}