Replaced DBObject.getId() in cache-file names by DBObject.getUniqueIdentifier().
[trakem2.git] / mpicbg / trakem2 / align / AlignTask.java
blob536d11eab8062fb4345abf5911d1ee599500c619
1 /**
2 *
3 */
4 package mpicbg.trakem2.align;
6 import java.awt.Rectangle;
7 import java.awt.geom.AffineTransform;
8 import java.util.ArrayList;
9 import java.util.Collection;
10 import java.util.List;
11 import java.util.Set;
13 import mpicbg.ij.FeatureTransform;
14 import mpicbg.ij.SIFT;
15 import mpicbg.imagefeatures.Feature;
16 import mpicbg.imagefeatures.FloatArray2DSIFT;
17 import mpicbg.models.AbstractAffineModel2D;
18 import mpicbg.models.AffineModel2D;
19 import mpicbg.models.NotEnoughDataPointsException;
20 import mpicbg.models.PointMatch;
21 import mpicbg.models.SimilarityModel2D;
22 import mpicbg.models.Tile;
23 import mpicbg.trakem2.transform.RigidModel2D;
24 import mpicbg.trakem2.transform.TranslationModel2D;
26 import ij.IJ;
27 import ij.ImagePlus;
28 import ij.gui.GenericDialog;
29 import ini.trakem2.display.Display;
30 import ini.trakem2.display.Displayable;
31 import ini.trakem2.display.Layer;
32 import ini.trakem2.display.Patch;
33 import ini.trakem2.display.Selection;
34 import ini.trakem2.utils.Worker;
35 import ini.trakem2.utils.Bureaucrat;
36 import ini.trakem2.utils.IJError;
37 import ini.trakem2.utils.Utils;
39 /**
40 * Methods collection to be called from the GUI for alignment tasks.
43 final public class AlignTask
45 final static public Bureaucrat alignSelectionTask ( final Selection selection )
47 Worker worker = new Worker("Aligning selected images", false, true) {
48 public void run() {
49 startedWorking();
50 try {
51 alignSelection( selection );
52 Display.repaint(selection.getLayer());
53 } catch (Throwable e) {
54 IJError.print(e);
55 } finally {
56 finishedWorking();
59 public void cleanup() {
60 if (!selection.isEmpty())
61 selection.getLayer().getParent().undoOneStep();
64 return Bureaucrat.createAndStart( worker, selection.getProject() );
67 final static public void alignSelection( final Selection selection )
69 List< Patch > patches = new ArrayList< Patch >();
70 for ( Displayable d : Display.getFront().getSelection().getSelected() )
71 if ( d instanceof Patch ) patches.add( ( Patch )d );
73 if ( patches.size() < 2 )
75 Utils.log("No images to align in the selection.");
76 return;
79 //final Align.ParamOptimize p = Align.paramOptimize;
80 final GenericDialog gd = new GenericDialog( "Align Selected Tiles" );
81 Align.paramOptimize.addFields( gd );
83 gd.addMessage( "Miscellaneous:" );
84 gd.addCheckbox( "tiles are rougly in place", false );
85 gd.addCheckbox( "consider largest graph only", false );
86 gd.addCheckbox( "hide tiles from non-largest graph", false );
87 gd.addCheckbox( "delete tiles from non-largest graph", false );
89 gd.showDialog();
90 if ( gd.wasCanceled() ) return;
92 Align.paramOptimize.readFields( gd );
93 final boolean tilesAreInPlace = gd.getNextBoolean();
94 final boolean largestGraphOnly = gd.getNextBoolean();
95 final boolean hideDisconnectedTiles = gd.getNextBoolean();
96 final boolean deleteDisconnectedTiles = gd.getNextBoolean();
98 final Align.ParamOptimize p = Align.paramOptimize.clone();
99 List< AbstractAffineTile2D< ? > > tiles = new ArrayList< AbstractAffineTile2D< ? > >();
100 List< AbstractAffineTile2D< ? > > fixedTiles = new ArrayList< AbstractAffineTile2D< ? > > ();
101 List< Patch > fixedPatches = new ArrayList< Patch >();
102 final Displayable active = selection.getActive();
103 if ( active != null && active instanceof Patch )
104 fixedPatches.add( ( Patch )active );
105 Align.tilesFromPatches( p, patches, fixedPatches, tiles, fixedTiles );
107 final List< AbstractAffineTile2D< ? >[] > tilePairs = new ArrayList< AbstractAffineTile2D< ? >[] >();
108 if ( tilesAreInPlace )
109 AbstractAffineTile2D.pairOverlappingTiles( tiles, tilePairs );
110 else
111 AbstractAffineTile2D.pairTiles( tiles, tilePairs );
113 Align.connectTilePairs( p, tiles, tilePairs, Runtime.getRuntime().availableProcessors() );
115 if ( Thread.currentThread().isInterrupted() ) return;
117 List< Set< Tile< ? > > > graphs = AbstractAffineTile2D.identifyConnectedGraphs( tiles );
119 final List< AbstractAffineTile2D< ? > > interestingTiles;
120 if ( largestGraphOnly )
122 /** Find largest graph. */
123 Set< Tile< ? > > largestGraph = null;
124 for ( Set< Tile< ? > > graph : graphs )
125 if ( largestGraph == null || largestGraph.size() < graph.size() )
126 largestGraph = graph;
128 interestingTiles = new ArrayList< AbstractAffineTile2D< ? > >();
129 for ( Tile< ? > t : largestGraph )
130 interestingTiles.add( ( AbstractAffineTile2D< ? > )t );
132 if ( hideDisconnectedTiles )
133 for ( AbstractAffineTile2D< ? > t : tiles )
134 if ( !interestingTiles.contains( t ) )
135 t.getPatch().setVisible( false );
136 if ( deleteDisconnectedTiles )
137 for ( AbstractAffineTile2D< ? > t : tiles )
138 if ( !interestingTiles.contains( t ) )
139 t.getPatch().remove( false );
141 else
142 interestingTiles = tiles;
144 if ( Thread.currentThread().isInterrupted() ) return;
146 Align.optimizeTileConfiguration( p, interestingTiles, fixedTiles );
148 for ( AbstractAffineTile2D< ? > t : tiles )
149 t.getPatch().setAffineTransform( t.getModel().createAffine() );
152 final static public Bureaucrat alignLayersLinearlyTask ( final Layer l )
154 Worker worker = new Worker("Aligning layers", false, true) {
155 public void run() {
156 startedWorking();
157 try {
158 alignLayersLinearly(l);
159 } catch (Throwable e) {
160 IJError.print(e);
161 } finally {
162 finishedWorking();
166 return Bureaucrat.createAndStart(worker, l.getProject());
169 /** Accept optional worker to watch for graceful task interruption. */
170 final static public void alignLayersLinearly( final Layer l )
172 final List< Layer > layers = l.getParent().getLayers();
173 final String[] layerTitles = new String[ layers.size() ];
174 for ( int i = 0; i < layers.size(); ++i )
175 layerTitles[ i ] = layers.get( i ).getTitle();
177 //Param p = Align.param;
178 Align.param.sift.maxOctaveSize = 1600;
180 final GenericDialog gd = new GenericDialog( "Align Layers Linearly" );
182 gd.addMessage( "Layer Range:" );
183 gd.addChoice( "first :", layerTitles, l.getTitle() );
184 gd.addChoice( "last :", layerTitles, l.getTitle() );
185 Align.param.addFields( gd );
187 gd.addMessage( "Miscellaneous:" );
188 gd.addCheckbox( "propagate after last transform", false );
190 gd.showDialog();
191 if ( gd.wasCanceled() ) return;
193 final int first = gd.getNextChoiceIndex();
194 final int last = gd.getNextChoiceIndex();
195 final int d = first < last ? 1 : -1;
197 Align.param.readFields( gd );
198 final boolean propagateTransform = gd.getNextBoolean();
200 final Align.Param p = Align.param.clone();
201 final Rectangle box = layers.get( 0 ).getParent().getMinimalBoundingBox( Patch.class );
202 final float scale = Math.min( 1.0f, Math.min( ( float )p.sift.maxOctaveSize / ( float )box.width, ( float )p.sift.maxOctaveSize / ( float )box.height ) );
203 p.maxEpsilon *= scale;
205 final List< Layer > layerRange = new ArrayList< Layer >();
206 for ( int i = first; i != last + d; i += d )
207 layerRange.add( layers.get( i ) );
209 final FloatArray2DSIFT sift = new FloatArray2DSIFT( p.sift );
210 final SIFT ijSIFT = new SIFT( sift );
212 Rectangle box1 = null;
213 Rectangle box2 = null;
214 final Collection< Feature > features1 = new ArrayList< Feature >();
215 final Collection< Feature > features2 = new ArrayList< Feature >();
216 List< PointMatch > candidates = new ArrayList< PointMatch >();
217 List< PointMatch > inliers = new ArrayList< PointMatch >();
219 AffineTransform a = new AffineTransform();
221 int s = 0;
222 for ( final Layer layer : layerRange )
224 if ( Thread.currentThread().isInterrupted() ) break;
226 long t = System.currentTimeMillis();
228 features1.clear();
229 features1.addAll( features2 );
230 features2.clear();
232 final Rectangle box3 = layer.getMinimalBoundingBox( Patch.class );
234 if ( box3 == null || ( box.width == 0 && box.height == 0 ) ) continue;
236 box1 = box2;
237 box2 = box3;
239 ijSIFT.extractFeatures(
240 layer.getProject().getLoader().getFlatImage( layer, box2, scale, 0xffffffff, ImagePlus.GRAY8, Patch.class, true ).getProcessor(),
241 features2 );
242 IJ.log( features2.size() + " features extracted in layer \"" + layer.getTitle() + "\" (took " + ( System.currentTimeMillis() - t ) + " ms)." );
244 if ( features1.size() > 0 )
246 t = System.currentTimeMillis();
248 candidates.clear();
250 FeatureTransform.matchFeatures(
251 features2,
252 features1,
253 candidates,
254 p.rod );
256 final AbstractAffineModel2D< ? > model;
257 switch ( p.expectedModelIndex )
259 case 0:
260 model = new TranslationModel2D();
261 break;
262 case 1:
263 model = new RigidModel2D();
264 break;
265 case 2:
266 model = new SimilarityModel2D();
267 break;
268 case 3:
269 model = new AffineModel2D();
270 break;
271 default:
272 return;
275 boolean modelFound;
278 modelFound = model.filterRansac(
279 candidates,
280 inliers,
281 1000,
282 p.maxEpsilon,
283 p.minInlierRatio,
284 3 * model.getMinNumMatches(),
285 3 );
287 catch ( NotEnoughDataPointsException e )
289 modelFound = false;
292 if ( modelFound )
294 IJ.log( "Model found for layer \"" + layer.getTitle() + "\" and its predecessor:\n correspondences " + inliers.size() + " of " + candidates.size() + "\n average residual error " + ( model.getCost() / scale ) + " px\n took " + ( System.currentTimeMillis() - t ) + " ms" );
295 final AffineTransform b = new AffineTransform();
296 b.translate( box1.x, box1.y );
297 b.scale( 1.0f / scale, 1.0f / scale );
298 b.concatenate( model.createAffine() );
299 b.scale( scale, scale );
300 b.translate( -box2.x, -box2.y);
302 a.concatenate( b );
303 layer.apply( Displayable.class, a );
304 Display.repaint( layer );
306 else
307 IJ.log( "No model found for layer \"" + layer.getTitle() + "\" and its predecessor." );
309 IJ.showProgress( ++s, layerRange.size() );
311 if ( propagateTransform )
313 for ( int i = last + d; i >= 0 && i < layers.size(); i += d )
314 layers.get( i ).apply( Displayable.class, a );