4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License 2
6 * as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 package mpicbg
.trakem2
.transform
;
19 import ij
.process
.ByteProcessor
;
20 import ij
.process
.ImageProcessor
;
21 import ij
.process
.ShortProcessor
;
23 import java
.util
.ArrayList
;
24 import java
.util
.HashMap
;
25 import java
.util
.List
;
26 import java
.util
.concurrent
.atomic
.AtomicInteger
;
28 import mpicbg
.models
.AffineModel2D
;
29 import mpicbg
.models
.PointMatch
;
30 import mpicbg
.models
.TransformMesh
;
31 import mpicbg
.util
.Util
;
34 * Specialized {@link mpicbg.ij.TransformMapping} for Patches, that is, rendering
35 * the image, outside mask and mask in one go instead three.
37 * @author Stephan Saalfeld <saalfeld@mpi-cbg.de>
40 public class TransformMeshMappingWithMasks
< T
extends TransformMesh
> extends mpicbg
.ij
.TransformMeshMapping
< T
>
42 final static public class ImageProcessorWithMasks
44 final public ImageProcessor ip
;
45 public ByteProcessor outside
= null;
46 public ImageProcessor mask
= null;
48 public ImageProcessorWithMasks( final ImageProcessor ip
, final ImageProcessor mask
, final ByteProcessor outside
)
51 if ( outside
!= null )
53 if ( ip
.getWidth() == outside
.getWidth() && ip
.getHeight() == outside
.getHeight() )
54 this.outside
= outside
;
56 System
.err
.println( "ImageProcessorWithMasks: ip and outside mask differ in size, setting outside = null" );
60 if ( ip
.getWidth() == mask
.getWidth() && ip
.getHeight() == mask
.getHeight() )
63 System
.err
.println( "ImageProcessorWithMasks: ip and mask differ in size, setting mask = null" );
67 final public int getWidth(){ return ip
.getWidth(); }
68 final public int getHeight(){ return ip
.getHeight(); }
71 final static private class MapTriangleThread
extends Thread
73 final private AtomicInteger i
;
74 final private List
< AffineModel2D
> triangles
;
75 final private TransformMesh transform
;
76 final ImageProcessorWithMasks source
, target
;
78 final AtomicInteger i
,
79 final List
< AffineModel2D
> triangles
,
80 final TransformMesh transform
,
81 final ImageProcessorWithMasks source
,
82 final ImageProcessorWithMasks target
)
85 this.triangles
= triangles
;
86 this.transform
= transform
;
92 final public void run()
94 int k
= i
.getAndIncrement();
95 while ( !isInterrupted() && k
< triangles
.size() )
97 if ( source
.mask
== null )
98 mapTriangle( transform
, triangles
.get( k
), source
.ip
, target
.ip
, target
.outside
);
100 mapTriangle( transform
, triangles
.get( k
), source
.ip
, source
.mask
, target
.ip
, target
.mask
, target
.outside
);
101 k
= i
.getAndIncrement();
106 final static private class MapTriangleInterpolatedThread
extends Thread
108 final private AtomicInteger i
;
109 final private List
< AffineModel2D
> triangles
;
110 final private TransformMesh transform
;
111 final ImageProcessorWithMasks source
, target
;
112 MapTriangleInterpolatedThread(
113 final AtomicInteger i
,
114 final List
< AffineModel2D
> triangles
,
115 final TransformMesh transform
,
116 final ImageProcessorWithMasks source
,
117 final ImageProcessorWithMasks target
)
120 this.triangles
= triangles
;
121 this.transform
= transform
;
122 this.source
= source
;
123 this.target
= target
;
127 final public void run()
129 int k
= i
.getAndIncrement();
130 while ( !isInterrupted() && k
< triangles
.size() )
132 if ( source
.mask
== null )
133 mapTriangleInterpolated( transform
, triangles
.get( k
), source
.ip
, target
.ip
, target
.outside
);
135 mapTriangleInterpolated( transform
, triangles
.get( k
), source
.ip
, source
.mask
, target
.ip
, target
.mask
, target
.outside
);
136 k
= i
.getAndIncrement();
141 final static private class MapShortAlphaTriangleThread
extends Thread
143 final private AtomicInteger i
;
144 final private List
< AffineModel2D
> triangles
;
145 final private TransformMesh transform
;
146 final ShortProcessor source
, target
;
147 final ByteProcessor alpha
;
148 MapShortAlphaTriangleThread(
149 final AtomicInteger i
,
150 final List
< AffineModel2D
> triangles
,
151 final TransformMesh transform
,
152 final ShortProcessor source
,
153 final ByteProcessor alpha
,
154 final ShortProcessor target
)
157 this.triangles
= triangles
;
158 this.transform
= transform
;
159 this.source
= source
;
161 this.target
= target
;
165 final public void run()
167 int k
= i
.getAndIncrement();
168 while ( !isInterrupted() && k
< triangles
.size() )
170 mapShortAlphaTriangle( transform
, triangles
.get( k
), source
, alpha
, target
);
171 k
= i
.getAndIncrement();
176 public TransformMeshMappingWithMasks( final T t
)
181 final static protected void mapTriangle(
182 final TransformMesh m
,
183 final AffineModel2D ai
,
184 final ImageProcessor source
,
185 final ImageProcessor target
,
186 final ByteProcessor targetOutside
)
188 final int w
= target
.getWidth() - 1;
189 final int h
= target
.getHeight() - 1;
190 final ArrayList
< PointMatch
> pm
= m
.getAV().get( ai
);
191 final float[] min
= new float[ 2 ];
192 final float[] max
= new float[ 2 ];
193 calculateBoundingBox( pm
, min
, max
);
195 final int minX
= Math
.max( 0, Util
.roundPos( min
[ 0 ] ) );
196 final int minY
= Math
.max( 0, Util
.roundPos( min
[ 1 ] ) );
197 final int maxX
= Math
.min( w
, Util
.roundPos( max
[ 0 ] ) );
198 final int maxY
= Math
.min( h
, Util
.roundPos( max
[ 1 ] ) );
200 final float[] a
= pm
.get( 0 ).getP2().getW();
201 final float ax
= a
[ 0 ];
202 final float ay
= a
[ 1 ];
203 final float[] b
= pm
.get( 1 ).getP2().getW();
204 final float bx
= b
[ 0 ];
205 final float by
= b
[ 1 ];
206 final float[] c
= pm
.get( 2 ).getP2().getW();
207 final float cx
= c
[ 0 ];
208 final float cy
= c
[ 1 ];
209 final float[] t
= new float[ 2 ];
210 for ( int y
= minY
; y
<= maxY
; ++y
)
212 for ( int x
= minX
; x
<= maxX
; ++x
)
214 if ( isInTriangle( ax
, ay
, bx
, by
, cx
, cy
, x
, y
) )
220 ai
.applyInverseInPlace( t
);
222 catch ( final Exception e
)
224 //e.printStackTrace( System.err );
227 target
.set( x
, y
, source
.getPixel( ( int )( t
[ 0 ] + 0.5f
), ( int )( t
[ 1 ] + 0.5f
) ) );
228 targetOutside
.set( x
, y
, 0xff );
234 final static protected void mapTriangleInterpolated(
235 final TransformMesh m
,
236 final AffineModel2D ai
,
237 final ImageProcessor source
,
238 final ImageProcessor target
,
239 final ByteProcessor targetOutside
)
241 final int w
= target
.getWidth() - 1;
242 final int h
= target
.getHeight() - 1;
243 final ArrayList
< PointMatch
> pm
= m
.getAV().get( ai
);
244 final float[] min
= new float[ 2 ];
245 final float[] max
= new float[ 2 ];
246 calculateBoundingBox( pm
, min
, max
);
248 final int minX
= Math
.max( 0, Util
.roundPos( min
[ 0 ] ) );
249 final int minY
= Math
.max( 0, Util
.roundPos( min
[ 1 ] ) );
250 final int maxX
= Math
.min( w
, Util
.roundPos( max
[ 0 ] ) );
251 final int maxY
= Math
.min( h
, Util
.roundPos( max
[ 1 ] ) );
253 final float[] a
= pm
.get( 0 ).getP2().getW();
254 final float ax
= a
[ 0 ];
255 final float ay
= a
[ 1 ];
256 final float[] b
= pm
.get( 1 ).getP2().getW();
257 final float bx
= b
[ 0 ];
258 final float by
= b
[ 1 ];
259 final float[] c
= pm
.get( 2 ).getP2().getW();
260 final float cx
= c
[ 0 ];
261 final float cy
= c
[ 1 ];
262 final float[] t
= new float[ 2 ];
263 for ( int y
= minY
; y
<= maxY
; ++y
)
265 for ( int x
= minX
; x
<= maxX
; ++x
)
267 if ( isInTriangle( ax
, ay
, bx
, by
, cx
, cy
, x
, y
) )
273 ai
.applyInverseInPlace( t
);
275 catch ( final Exception e
)
277 //e.printStackTrace( System.err );
280 target
.set( x
, y
, source
.getPixelInterpolated( t
[ 0 ], t
[ 1 ] ) );
281 targetOutside
.set( x
, y
, 0xff );
288 final static protected void mapTriangle(
289 final TransformMesh m
,
290 final AffineModel2D ai
,
291 final ImageProcessor source
,
292 final ImageProcessor sourceMask
,
293 final ImageProcessor target
,
294 final ImageProcessor targetMask
,
295 final ByteProcessor targetOutside
)
297 final int w
= target
.getWidth() - 1;
298 final int h
= target
.getHeight() - 1;
299 final ArrayList
< PointMatch
> pm
= m
.getAV().get( ai
);
300 final float[] min
= new float[ 2 ];
301 final float[] max
= new float[ 2 ];
302 calculateBoundingBox( pm
, min
, max
);
304 final int minX
= Math
.max( 0, Util
.roundPos( min
[ 0 ] ) );
305 final int minY
= Math
.max( 0, Util
.roundPos( min
[ 1 ] ) );
306 final int maxX
= Math
.min( w
, Util
.roundPos( max
[ 0 ] ) );
307 final int maxY
= Math
.min( h
, Util
.roundPos( max
[ 1 ] ) );
309 final float[] a
= pm
.get( 0 ).getP2().getW();
310 final float ax
= a
[ 0 ];
311 final float ay
= a
[ 1 ];
312 final float[] b
= pm
.get( 1 ).getP2().getW();
313 final float bx
= b
[ 0 ];
314 final float by
= b
[ 1 ];
315 final float[] c
= pm
.get( 2 ).getP2().getW();
316 final float cx
= c
[ 0 ];
317 final float cy
= c
[ 1 ];
318 final float[] t
= new float[ 2 ];
319 for ( int y
= minY
; y
<= maxY
; ++y
)
321 for ( int x
= minX
; x
<= maxX
; ++x
)
323 if ( isInTriangle( ax
, ay
, bx
, by
, cx
, cy
, x
, y
) )
329 ai
.applyInverseInPlace( t
);
331 catch ( final Exception e
)
333 //e.printStackTrace( System.err );
336 target
.set( x
, y
, source
.getPixel( ( int )( t
[ 0 ] + 0.5f
), ( int )( t
[ 1 ] + 0.5f
) ) );
337 targetOutside
.set( x
, y
, 0xff );
338 targetMask
.set( x
, y
, sourceMask
.getPixel( ( int )( t
[ 0 ] + 0.5f
), ( int )( t
[ 1 ] + 0.5f
) ) );
344 final static protected void mapTriangleInterpolated(
345 final TransformMesh m
,
346 final AffineModel2D ai
,
347 final ImageProcessor source
,
348 final ImageProcessor sourceMask
,
349 final ImageProcessor target
,
350 final ImageProcessor targetMask
,
351 final ByteProcessor targetOutside
)
353 final int w
= target
.getWidth() - 1;
354 final int h
= target
.getHeight() - 1;
355 final ArrayList
< PointMatch
> pm
= m
.getAV().get( ai
);
356 final float[] min
= new float[ 2 ];
357 final float[] max
= new float[ 2 ];
358 calculateBoundingBox( pm
, min
, max
);
360 final int minX
= Math
.max( 0, Util
.roundPos( min
[ 0 ] ) );
361 final int minY
= Math
.max( 0, Util
.roundPos( min
[ 1 ] ) );
362 final int maxX
= Math
.min( w
, Util
.roundPos( max
[ 0 ] ) );
363 final int maxY
= Math
.min( h
, Util
.roundPos( max
[ 1 ] ) );
365 final float[] a
= pm
.get( 0 ).getP2().getW();
366 final float ax
= a
[ 0 ];
367 final float ay
= a
[ 1 ];
368 final float[] b
= pm
.get( 1 ).getP2().getW();
369 final float bx
= b
[ 0 ];
370 final float by
= b
[ 1 ];
371 final float[] c
= pm
.get( 2 ).getP2().getW();
372 final float cx
= c
[ 0 ];
373 final float cy
= c
[ 1 ];
374 final float[] t
= new float[ 2 ];
375 for ( int y
= minY
; y
<= maxY
; ++y
)
377 for ( int x
= minX
; x
<= maxX
; ++x
)
379 if ( isInTriangle( ax
, ay
, bx
, by
, cx
, cy
, x
, y
) )
385 ai
.applyInverseInPlace( t
);
387 catch ( final Exception e
)
389 //e.printStackTrace( System.err );
392 target
.set( x
, y
, source
.getPixelInterpolated( t
[ 0 ], t
[ 1 ] ) );
393 targetOutside
.set( x
, y
, 0xff );
394 targetMask
.set( x
, y
, sourceMask
.getPixelInterpolated( t
[ 0 ], t
[ 1 ] ) );
401 final static protected void mapShortAlphaTriangle(
402 final TransformMesh m
,
403 final AffineModel2D ai
,
404 final ShortProcessor source
,
405 final ByteProcessor alpha
,
406 final ShortProcessor target
)
408 final int w
= target
.getWidth() - 1;
409 final int h
= target
.getHeight() - 1;
410 final ArrayList
< PointMatch
> pm
= m
.getAV().get( ai
);
411 final float[] min
= new float[ 2 ];
412 final float[] max
= new float[ 2 ];
413 calculateBoundingBox( pm
, min
, max
);
415 final int minX
= Math
.max( 0, Util
.roundPos( min
[ 0 ] ) );
416 final int minY
= Math
.max( 0, Util
.roundPos( min
[ 1 ] ) );
417 final int maxX
= Math
.min( w
, Util
.roundPos( max
[ 0 ] ) );
418 final int maxY
= Math
.min( h
, Util
.roundPos( max
[ 1 ] ) );
420 final float[] a
= pm
.get( 0 ).getP2().getW();
421 final float ax
= a
[ 0 ];
422 final float ay
= a
[ 1 ];
423 final float[] b
= pm
.get( 1 ).getP2().getW();
424 final float bx
= b
[ 0 ];
425 final float by
= b
[ 1 ];
426 final float[] c
= pm
.get( 2 ).getP2().getW();
427 final float cx
= c
[ 0 ];
428 final float cy
= c
[ 1 ];
429 final float[] t
= new float[ 2 ];
430 for ( int y
= minY
; y
<= maxY
; ++y
)
432 for ( int x
= minX
; x
<= maxX
; ++x
)
434 if ( isInTriangle( ax
, ay
, bx
, by
, cx
, cy
, x
, y
) )
440 ai
.applyInverseInPlace( t
);
442 catch ( final Exception e
)
444 //e.printStackTrace( System.err );
447 final int is
= source
.getPixelInterpolated( t
[ 0 ], t
[ 1 ] );
448 final int it
= target
.get( x
, y
);
449 final double f
= alpha
.getPixelInterpolated( t
[ 0 ], t
[ 1 ] ) / 255.0;
450 final double v
= it
+ f
* ( is
- it
);
451 target
.set( x
, y
, ( int )Math
.max( 0, Math
.min( 65535, Math
.round( v
) ) ) );
458 final public void map( final ImageProcessorWithMasks source
, final ImageProcessorWithMasks target
, final int numThreads
)
460 target
.outside
= new ByteProcessor( target
.getWidth(), target
.getHeight() );
461 final HashMap
< AffineModel2D
, ArrayList
< PointMatch
>> av
= transform
.getAV();
462 if ( numThreads
> 1 )
464 final ArrayList
< AffineModel2D
> triangles
= new ArrayList
< AffineModel2D
>( av
.keySet() );
465 final AtomicInteger i
= new AtomicInteger( 0 );
466 final ArrayList
< Thread
> threads
= new ArrayList
< Thread
>( numThreads
);
467 for ( int k
= 0; k
< numThreads
; ++k
)
469 final Thread mtt
= new MapTriangleThread( i
, triangles
, transform
, source
, target
);
473 for ( final Thread mtt
: threads
)
479 catch ( final InterruptedException e
)
483 else if ( source
.mask
== null )
485 for ( final AffineModel2D triangle
: av
.keySet() )
487 mapTriangle( transform
, triangle
, source
.ip
, target
.ip
, target
.outside
);
492 for ( final AffineModel2D triangle
: av
.keySet() )
494 mapTriangle( transform
, triangle
, source
.ip
, source
.mask
, target
.ip
, target
.mask
, target
.outside
);
499 final public void mapInterpolated( final ImageProcessorWithMasks source
, final ImageProcessorWithMasks target
, final int numThreads
)
501 target
.outside
= new ByteProcessor( target
.getWidth(), target
.getHeight() );
502 source
.ip
.setInterpolationMethod( ImageProcessor
.BILINEAR
);
503 if ( source
.mask
!= null )
505 source
.mask
.setInterpolationMethod( ImageProcessor
.BILINEAR
);
507 final HashMap
< AffineModel2D
, ArrayList
< PointMatch
>> av
= transform
.getAV();
508 if ( numThreads
> 1 )
510 final ArrayList
< AffineModel2D
> triangles
= new ArrayList
< AffineModel2D
>( av
.keySet() );
511 final AtomicInteger i
= new AtomicInteger( 0 );
512 final ArrayList
< Thread
> threads
= new ArrayList
< Thread
>( numThreads
);
513 for ( int k
= 0; k
< numThreads
; ++k
)
515 final Thread mtt
= new MapTriangleInterpolatedThread( i
, triangles
, transform
, source
, target
);
519 for ( final Thread mtt
: threads
)
525 catch ( final InterruptedException e
)
529 else if ( source
.mask
== null )
531 for ( final AffineModel2D triangle
: av
.keySet() )
533 mapTriangleInterpolated( transform
, triangle
, source
.ip
, target
.ip
, target
.outside
);
538 for ( final AffineModel2D triangle
: av
.keySet() )
540 mapTriangleInterpolated( transform
, triangle
, source
.ip
, source
.mask
, target
.ip
, target
.mask
, target
.outside
);
545 final public void map(
546 final ImageProcessorWithMasks source
,
547 final ImageProcessorWithMasks target
)
549 map( source
, target
, Runtime
.getRuntime().availableProcessors() );
552 final public void mapInterpolated(
553 final ImageProcessorWithMasks source
,
554 final ImageProcessorWithMasks target
)
556 mapInterpolated( source
, target
, Runtime
.getRuntime().availableProcessors() );
561 * Render source into target using alpha composition.
562 * Interpolation is specified by the interpolation methods
563 * set in source and alpha.
570 final public void map(
571 final ShortProcessor source
,
572 final ByteProcessor alpha
,
573 final ShortProcessor target
,
574 final int numThreads
)
576 final List
< AffineModel2D
> l
= new ArrayList
< AffineModel2D
>();
577 l
.addAll( transform
.getAV().keySet() );
578 final AtomicInteger i
= new AtomicInteger( 0 );
579 final ArrayList
< Thread
> threads
= new ArrayList
< Thread
>( numThreads
);
580 for ( int k
= 0; k
< numThreads
; ++k
)
582 final Thread mtt
= new MapShortAlphaTriangleThread( i
, l
, transform
, source
, alpha
, target
);
586 for ( final Thread mtt
: threads
)
592 catch ( final InterruptedException e
) {}
598 * Render source into master using alpha composition.
599 * Interpolation is specified by the interpolation methods
600 * set in source and alpha.
606 final public void map(
607 final ShortProcessor source
,
608 final ByteProcessor alpha
,
609 final ShortProcessor target
)
611 map( source
, alpha
, target
, Runtime
.getRuntime().availableProcessors() );