/*-
* #%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.interestpointregistration;
import ij.gui.GenericDialog;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import mpicbg.spim.data.sequence.ViewDescription;
import mpicbg.spim.data.sequence.ViewId;
import mpicbg.spim.io.IOFunctions;
import spim.Threads;
import spim.fiji.plugin.Interest_Point_Registration.RegistrationType;
import spim.fiji.spimdata.SpimData2;
import spim.process.interestpointregistration.ChannelProcess;
import spim.process.interestpointregistration.PairwiseMatch;
import spim.process.interestpointregistration.TransformationModel;
import spim.process.interestpointregistration.optimizationtypes.GlobalOptimizationSubset;
import spim.process.interestpointregistration.optimizationtypes.GlobalOptimizationType;
/**
*
* @author Stephan Preibisch (stephan.preibisch@gmx.de)
*
*/
public abstract class InterestPointRegistration
{
final SpimData2 spimData1;
final List< ViewId > viewIdsToProcess;
final List< ChannelProcess > channelsToProcess;
List< List< PairwiseMatch > > statistics;
/**
* Instantiate the interest point registration. It is performed for a spimdata object on a
* subset of angles, channels, illuminations and timepoints. Each channel is linked to a
* certain type of detections (e.g. beads, nuclei), hence the {@link ChannelProcess} object.
*
* @param spimData
* @param viewIdsToProcess - which view id's to register
* @param channelsToProcess - which Channel uses which label for registration
*/
public InterestPointRegistration( final SpimData2 spimData, final List< ViewId > viewIdsToProcess, final List< ChannelProcess > channelsToProcess )
{
this.spimData1 = spimData;
this.viewIdsToProcess = viewIdsToProcess;
this.channelsToProcess = channelsToProcess;
}
/**
* adds the questions this registration wants to ask
*
* @param gd
* @param registrationType - which kind of registration
*/
public abstract void addQuery( final GenericDialog gd, final RegistrationType registrationType );
/**
* queries the questions asked before
*
* @param gd
* @param registrationType - which kind of timeseries registration
* @return
*/
public abstract boolean parseDialog( final GenericDialog gd, final RegistrationType registrationType );
/**
* @return - a new instance without any special properties
*/
public abstract InterestPointRegistration newInstance( final SpimData2 spimData, final List< ViewId > viewIdsToProcess, final List< ChannelProcess > channelsToProcess );
/**
* @return - to be displayed in the generic dialog
*/
public abstract String getDescription();
/**
* @param pair - which pair to compare
* @param description - a description String which pairs are compared
* @return - the object that will perform a pairwise matching and can return a result
*/
protected abstract Callable< PairwiseMatch > pairwiseMatchingInstance( final PairwiseMatch pair, final String description );
/**
* @return - the transformation model to be used for the global optimization, and in most cases also for RANSAC
*/
protected abstract TransformationModel getTransformationModel();
/**
* Run the global optimization on the subset
*
* @param subset
* @param registrationType
* @return - true if the global optimization could be run successfully, otherwise false (XML will not be saved if false)
*/
@SuppressWarnings("unchecked")
final protected boolean runGlobalOpt(
final GlobalOptimizationSubset subset,
final GlobalOptimizationType registrationType )
{
return subset.computeGlobalOpt(
getTransformationModel().getModel(),
registrationType,
getSpimData(),
getChannelsToProcess(),
getDescription() + ", " + getTransformationModel().getDescription() );
}
protected SpimData2 getSpimData() { return spimData1; }
public List< ViewId > getViewIdsToProcess() { return viewIdsToProcess; }
public List< ChannelProcess > getChannelsToProcess() { return channelsToProcess; }
public List< List< PairwiseMatch > > getStatistics() { return statistics; }
/**
* Registers all timepoints. No matter which matching is done it is always the same principle.
*
* First all pairwise correspondences are established, and then a global optimization is computed.
* The global optimization can is done in subsets, where the number of subsets >= 1.
*
* @param registrationType - which kind of registration
* @param save - if you want to save the correspondence files
* @return
*/
public boolean register( final GlobalOptimizationType registrationType, final boolean save, final boolean collectStatistics )
{
final SpimData2 spimData = getSpimData();
IOFunctions.println( "(" + new Date( System.currentTimeMillis() ) + "): Starting registration" );
if ( collectStatistics )
this.statistics = new ArrayList< List< PairwiseMatch > >();
// get a list of all pairs for this specific GlobalOptimizationType
final List< GlobalOptimizationSubset > list = registrationType.getAllViewPairs();
int successfulRuns = 0;
for ( final GlobalOptimizationSubset subset : list )
{
IOFunctions.println( "(" + new Date( System.currentTimeMillis() ) + "): Finding correspondences for subset: " + subset.getDescription() );
final List< PairwiseMatch > pairs = subset.getViewPairs();
final ExecutorService taskExecutor = Executors.newFixedThreadPool( Threads.numThreads() );
final ArrayList< Callable< PairwiseMatch > > tasks = new ArrayList< Callable< PairwiseMatch > >(); // your tasks
for ( final PairwiseMatch pair : pairs )
{
// just for logging the names and results of pairwise comparison
final ViewDescription viewA = spimData.getSequenceDescription().getViewDescription( pair.getViewIdA() );
final ViewDescription viewB = spimData.getSequenceDescription().getViewDescription( pair.getViewIdB() );
final String description = "[TP=" + viewA.getTimePoint().getName() +
" angle=" + viewA.getViewSetup().getAngle().getName() + ", ch=" + viewA.getViewSetup().getChannel().getName() +
", illum=" + viewA.getViewSetup().getIllumination().getName() + " >>> TP=" + viewB.getTimePoint().getName() +
" angle=" + viewB.getViewSetup().getAngle().getName() + ", ch=" + viewB.getViewSetup().getChannel().getName() +
", illum=" + viewB.getViewSetup().getIllumination().getName() + "]";
tasks.add( pairwiseMatchingInstance( pair, description ) );
}
try
{
// invokeAll() returns when all tasks are complete
taskExecutor.invokeAll( tasks );
}
catch ( final InterruptedException e )
{
IOFunctions.println( "Failed to compute registrations for " + subset.getDescription() );
e.printStackTrace();
}
// some statistics
int sumCandidates = 0;
int sumInliers = 0;
for ( final PairwiseMatch pair : pairs )
{
sumCandidates += pair.getCandidates().size();
sumInliers += pair.getInliers().size();
}
IOFunctions.println( "(" + new Date( System.currentTimeMillis() ) + "): Number of Candidates: " + sumCandidates );
IOFunctions.println( "(" + new Date( System.currentTimeMillis() ) + "): Number of Inliers: " + sumInliers );
if ( collectStatistics )
statistics.add( pairs );
//
// set and store correspondences
//
// first remove existing correspondences
registrationType.clearExistingCorrespondences( subset );
// now add all corresponding interest points
registrationType.addCorrespondences( pairs );
// save the files
if ( save )
registrationType.saveCorrespondences( subset );
if ( runGlobalOpt( subset, registrationType ) )
++successfulRuns;
}
if ( successfulRuns > 0 )
return true;
else
return false;
}
}