4 package lenscorrection
;
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
;
12 import java
.util
.concurrent
.atomic
.AtomicInteger
;
14 import lenscorrection
.Distortion_Correction
.BasicParam
;
15 import mpicbg
.models
.PointMatch
;
16 import mpicbg
.models
.Tile
;
17 import mpicbg
.trakem2
.align
.AbstractAffineTile2D
;
18 import mpicbg
.trakem2
.align
.Align
;
19 import mpicbg
.trakem2
.transform
.CoordinateTransform
;
22 import ij
.gui
.GenericDialog
;
23 import ini
.trakem2
.display
.Display
;
24 import ini
.trakem2
.display
.Displayable
;
25 import ini
.trakem2
.display
.Layer
;
26 import ini
.trakem2
.display
.Patch
;
27 import ini
.trakem2
.display
.Selection
;
28 import ini
.trakem2
.utils
.Worker
;
29 import ini
.trakem2
.utils
.Bureaucrat
;
30 import ini
.trakem2
.utils
.IJError
;
31 import ini
.trakem2
.utils
.Utils
;
34 * Methods collection to be called from the GUI for alignment tasks.
37 final public class DistortionCorrectionTask
39 static public class CorrectDistortionFromSelectionParam
extends BasicParam
41 public int firstLayerIndex
;
42 public int lastLayerIndex
;
43 public boolean clearTransform
= false;
44 public boolean visualize
= false;
46 public void addFields( final GenericDialog gd
, final Selection selection
)
49 gd
.addMessage( "Apply Distortion Correction :" );
51 Utils
.addLayerRangeChoices( selection
.getLayer(), gd
);
52 gd
.addCheckbox( "clear_present_transforms", clearTransform
);
53 gd
.addCheckbox( "visualize_distortion_model", visualize
);
57 public boolean readFields( final GenericDialog gd
)
59 super.readFields( gd
);
60 firstLayerIndex
= gd
.getNextChoiceIndex();
61 lastLayerIndex
= gd
.getNextChoiceIndex();
62 clearTransform
= gd
.getNextBoolean();
63 visualize
= gd
.getNextBoolean();
64 return !gd
.invalidNumber();
67 public boolean setup( final Selection selection
)
69 final GenericDialog gd
= new GenericDialog( "Distortion Correction" );
70 addFields( gd
, selection
);
74 if ( gd
.wasCanceled() ) return false;
76 while ( !readFields( gd
) );
82 public CorrectDistortionFromSelectionParam
clone()
84 final CorrectDistortionFromSelectionParam p
= new CorrectDistortionFromSelectionParam();
86 p
.dimension
= dimension
;
87 p
.expectedModelIndex
= expectedModelIndex
;
89 p
.maxEpsilon
= maxEpsilon
;
90 p
.minInlierRatio
= minInlierRatio
;
92 p
.firstLayerIndex
= firstLayerIndex
;
93 p
.lastLayerIndex
= lastLayerIndex
;
94 p
.clearTransform
= clearTransform
;
95 p
.visualize
= visualize
;
103 * Sets a {@link CoordinateTransform} to a list of patches.
105 final static protected class SetCoordinateTransformThread
extends Thread
107 final protected List
< Patch
> patches
;
108 final protected CoordinateTransform transform
;
109 final protected AtomicInteger ai
;
111 public SetCoordinateTransformThread(
112 final List
< Patch
> patches
,
113 final CoordinateTransform transform
,
114 final AtomicInteger ai
)
116 this.patches
= patches
;
117 this.transform
= transform
;
122 final public void run()
124 for ( int i
= ai
.getAndIncrement(); i
< patches
.size() && !isInterrupted(); i
= ai
.getAndIncrement() )
126 final Patch patch
= patches
.get( i
);
127 //IJ.log( "Setting transform \"" + transform + "\" for patch \"" + patch.getTitle() + "\"." );
128 patch
.setCoordinateTransform( transform
);
129 patch
.updateMipmaps();
131 IJ
.showProgress( i
, patches
.size() );
136 final static protected void setCoordinateTransform(
137 final List
< Patch
> patches
,
138 final CoordinateTransform transform
,
139 final int numThreads
)
141 final AtomicInteger ai
= new AtomicInteger( 0 );
142 final List
< SetCoordinateTransformThread
> threads
= new ArrayList
< SetCoordinateTransformThread
>();
144 for ( int i
= 0; i
< numThreads
; ++i
)
146 final SetCoordinateTransformThread thread
= new SetCoordinateTransformThread( patches
, transform
, ai
);
147 threads
.add( thread
);
152 for ( final Thread thread
: threads
)
155 catch ( InterruptedException e
)
157 IJ
.log( "Setting CoordinateTransform failed.\n" + e
.getMessage() + "\n" + e
.getStackTrace() );
163 * Appends a {@link CoordinateTransform} to a list of patches.
165 final static protected class AppendCoordinateTransformThread
extends Thread
167 final protected List
< Patch
> patches
;
168 final protected CoordinateTransform transform
;
169 final protected AtomicInteger ai
;
171 public AppendCoordinateTransformThread(
172 final List
< Patch
> patches
,
173 final CoordinateTransform transform
,
174 final AtomicInteger ai
)
176 this.patches
= patches
;
177 this.transform
= transform
;
182 final public void run()
184 for ( int i
= ai
.getAndIncrement(); i
< patches
.size() && !isInterrupted(); i
= ai
.getAndIncrement() )
186 final Patch patch
= patches
.get( i
);
187 patch
.appendCoordinateTransform( transform
);
188 patch
.updateMipmaps();
190 IJ
.showProgress( i
, patches
.size() );
195 final static protected void appendCoordinateTransform(
196 final List
< Patch
> patches
,
197 final CoordinateTransform transform
,
198 final int numThreads
)
200 final AtomicInteger ai
= new AtomicInteger( 0 );
201 final List
< AppendCoordinateTransformThread
> threads
= new ArrayList
< AppendCoordinateTransformThread
>();
203 for ( int i
= 0; i
< numThreads
; ++i
)
205 final AppendCoordinateTransformThread thread
= new AppendCoordinateTransformThread( patches
, transform
, ai
);
206 threads
.add( thread
);
211 for ( final Thread thread
: threads
)
214 catch ( InterruptedException e
)
216 IJ
.log( "Appending CoordinateTransform failed.\n" + e
.getMessage() + "\n" + e
.getStackTrace() );
220 final static public CorrectDistortionFromSelectionParam correctDistortionFromSelectionParam
= new CorrectDistortionFromSelectionParam();
222 final static public Bureaucrat
correctDistortionFromSelectionTask ( final Selection selection
)
224 Worker worker
= new Worker("Distortion Correction", false, true) {
228 correctDistortionFromSelection( selection
);
229 Display
.repaint(selection
.getLayer());
230 } catch (Throwable e
) {
236 public void cleanup() {
237 if (!selection
.isEmpty())
238 selection
.getLayer().getParent().undoOneStep();
241 return Bureaucrat
.createAndStart( worker
, selection
.getProject() );
244 final static public Bureaucrat
correctDistortionFromSelection( final Selection selection
)
246 final List
< Patch
> patches
= new ArrayList
< Patch
>();
247 for ( Displayable d
: Display
.getFront().getSelection().getSelected() )
248 if ( d
instanceof Patch
) patches
.add( ( Patch
)d
);
250 if ( patches
.size() < 2 )
252 Utils
.log("No images in the selection.");
256 final Worker worker
= new Worker( "Lens correction" )
258 final public void run()
264 if ( !correctDistortionFromSelectionParam
.setup( selection
) ) return;
266 final CorrectDistortionFromSelectionParam p
= correctDistortionFromSelectionParam
.clone();
267 final Align
.ParamOptimize ap
= Align
.paramOptimize
.clone();
268 ap
.sift
.set( p
.sift
);
269 ap
.desiredModelIndex
= ap
.expectedModelIndex
= p
.expectedModelIndex
;
270 ap
.maxEpsilon
= p
.maxEpsilon
;
271 ap
.minInlierRatio
= p
.minInlierRatio
;
274 /** Get all patches that will be affected. */
275 final List
< Patch
> allPatches
= new ArrayList
< Patch
>();
276 for ( final Layer l
: selection
.getLayer().getParent().getLayers().subList( p
.firstLayerIndex
, p
.lastLayerIndex
+ 1 ) )
277 for ( Displayable d
: l
.getDisplayables( Patch
.class ) )
278 allPatches
.add( ( Patch
)d
);
280 /** Unset the coordinate transforms of all patches if desired. */
281 if ( p
.clearTransform
)
283 setTaskName( "Clearing present transforms" );
284 setCoordinateTransform( allPatches
, null, Runtime
.getRuntime().availableProcessors() );
288 setTaskName( "Establishing SIFT correspondences" );
290 List
< AbstractAffineTile2D
< ?
> > tiles
= new ArrayList
< AbstractAffineTile2D
< ?
> >();
291 List
< AbstractAffineTile2D
< ?
> > fixedTiles
= new ArrayList
< AbstractAffineTile2D
< ?
> > ();
292 List
< Patch
> fixedPatches
= new ArrayList
< Patch
>();
293 final Displayable active
= selection
.getActive();
294 if ( active
!= null && active
instanceof Patch
)
295 fixedPatches
.add( ( Patch
)active
);
296 Align
.tilesFromPatches( ap
, patches
, fixedPatches
, tiles
, fixedTiles
);
298 final List
< AbstractAffineTile2D
< ?
>[] > tilePairs
= new ArrayList
< AbstractAffineTile2D
< ?
>[] >();
300 /** TODO Verena: Why not each to each? */
301 // AbstractAffineTile2D.pairTiles( tiles, tilePairs );
302 final AbstractAffineTile2D
< ?
> fixedTile
= fixedTiles
.iterator().next();
303 for ( final AbstractAffineTile2D
< ?
> t
: tiles
)
304 if ( t
!= fixedTile
)
305 tilePairs
.add( new AbstractAffineTile2D
< ?
>[]{ t
, fixedTile
} );
307 Align
.connectTilePairs( ap
, tiles
, tilePairs
, Runtime
.getRuntime().availableProcessors() );
310 /** Shift all local coordinates into the original image frame */
311 for ( final AbstractAffineTile2D
< ?
> tile
: tiles
)
313 final Rectangle box
= tile
.getPatch().getCoordinateTransformBoundingBox();
314 for ( final PointMatch m
: tile
.getMatches() )
316 final float[] l
= m
.getP1().getL();
317 final float[] w
= m
.getP1().getW();
325 if ( Thread
.currentThread().isInterrupted() ) return;
327 List
< Set
< Tile
< ?
> > > graphs
= AbstractAffineTile2D
.identifyConnectedGraphs( tiles
);
328 if ( graphs
.size() > 1 )
329 IJ
.log( "Could not interconnect all images with correspondences. " );
331 final List
< AbstractAffineTile2D
< ?
> > interestingTiles
;
333 /** Find largest graph. */
334 Set
< Tile
< ?
> > largestGraph
= null;
335 for ( Set
< Tile
< ?
> > graph
: graphs
)
336 if ( largestGraph
== null || largestGraph
.size() < graph
.size() )
337 largestGraph
= graph
;
339 interestingTiles
= new ArrayList
< AbstractAffineTile2D
< ?
> >();
340 for ( Tile
< ?
> t
: largestGraph
)
341 interestingTiles
.add( ( AbstractAffineTile2D
< ?
> )t
);
343 if ( Thread
.currentThread().isInterrupted() ) return;
345 Align
.optimizeTileConfiguration( ap
, interestingTiles
, fixedTiles
);
347 /** Some data shuffling for the lens correction interface */
348 final List
< Collection
< PointMatch
> > matches
= new ArrayList
< Collection
< PointMatch
> >();
349 final List
< AffineTransform
> affines
= new ArrayList
< AffineTransform
>();
350 for ( AbstractAffineTile2D
< ?
>[] tilePair
: tilePairs
)
352 matches
.add( tilePair
[ 0 ].getMatches() );
353 final AffineTransform a
= tilePair
[ 0 ].createAffine();
354 a
.preConcatenate( fixedTile
.getModel().createInverseAffine() );
358 setTaskName( "Estimating lens distortion correction" );
360 final NonLinearTransform lensModel
= Distortion_Correction
.createInverseDistortionModel(
365 ( int )fixedTile
.getWidth(),
366 ( int )fixedTile
.getHeight() );
370 setTaskName( "Visualizing lens distortion correction" );
371 lensModel
.visualizeSmall( p
.lambda
);
374 setTaskName( "Applying lens distortion correction" );
376 appendCoordinateTransform( allPatches
, lensModel
, Runtime
.getRuntime().availableProcessors() );
382 catch ( Exception e
) { IJError
.print( e
); }
383 finally { finishedWorking(); }
387 return Bureaucrat
.createAndStart( worker
, selection
.getProject() );