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
;
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
;
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
;
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) {
51 alignSelection( selection
);
52 Display
.repaint(selection
.getLayer());
53 } catch (Throwable e
) {
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.");
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 );
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
);
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 );
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) {
158 alignLayersLinearly(l
);
159 } catch (Throwable e
) {
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 );
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();
222 for ( final Layer layer
: layerRange
)
224 if ( Thread
.currentThread().isInterrupted() ) break;
226 long t
= System
.currentTimeMillis();
229 features1
.addAll( features2
);
232 final Rectangle box3
= layer
.getMinimalBoundingBox( Patch
.class );
234 if ( box3
== null || ( box
.width
== 0 && box
.height
== 0 ) ) continue;
239 ijSIFT
.extractFeatures(
240 layer
.getProject().getLoader().getFlatImage( layer
, box2
, scale
, 0xffffffff, ImagePlus
.GRAY8
, Patch
.class, true ).getProcessor(),
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();
250 FeatureTransform
.matchFeatures(
256 final AbstractAffineModel2D
< ?
> model
;
257 switch ( p
.expectedModelIndex
)
260 model
= new TranslationModel2D();
263 model
= new RigidModel2D();
266 model
= new SimilarityModel2D();
269 model
= new AffineModel2D();
278 modelFound
= model
.filterRansac(
284 3 * model
.getMinNumMatches(),
287 catch ( NotEnoughDataPointsException e
)
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
);
303 layer
.apply( Displayable
.class, a
);
304 Display
.repaint( layer
);
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
);