Added Eric Trautman's check for number of threads to prevent
[trakem2.git] / mpicbg-trakem2 / src / main / java / mpicbg / trakem2 / transform / TransformMeshMappingWithMasks.java
blobc9de0e02bb4e31c165727bfc0330907d7ea87708
1 /**
2 * License: GPL
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;
33 /**
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>
38 * @version 0.1a
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 )
50 this.ip = ip;
51 if ( outside != null )
53 if ( ip.getWidth() == outside.getWidth() && ip.getHeight() == outside.getHeight() )
54 this.outside = outside;
55 else
56 System.err.println( "ImageProcessorWithMasks: ip and outside mask differ in size, setting outside = null" );
58 if ( mask != null )
60 if ( ip.getWidth() == mask.getWidth() && ip.getHeight() == mask.getHeight() )
61 this.mask = mask;
62 else
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;
77 MapTriangleThread(
78 final AtomicInteger i,
79 final List< AffineModel2D > triangles,
80 final TransformMesh transform,
81 final ImageProcessorWithMasks source,
82 final ImageProcessorWithMasks target )
84 this.i = i;
85 this.triangles = triangles;
86 this.transform = transform;
87 this.source = source;
88 this.target = target;
91 @Override
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 );
99 else
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 )
119 this.i = i;
120 this.triangles = triangles;
121 this.transform = transform;
122 this.source = source;
123 this.target = target;
126 @Override
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 );
134 else
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 )
156 this.i = i;
157 this.triangles = triangles;
158 this.transform = transform;
159 this.source = source;
160 this.alpha = alpha;
161 this.target = target;
164 @Override
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 )
178 super( 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 ) )
216 t[ 0 ] = x;
217 t[ 1 ] = y;
220 ai.applyInverseInPlace( t );
222 catch ( final Exception e )
224 //e.printStackTrace( System.err );
225 continue;
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 ) )
269 t[ 0 ] = x;
270 t[ 1 ] = y;
273 ai.applyInverseInPlace( t );
275 catch ( final Exception e )
277 //e.printStackTrace( System.err );
278 continue;
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 ) )
325 t[ 0 ] = x;
326 t[ 1 ] = y;
329 ai.applyInverseInPlace( t );
331 catch ( final Exception e )
333 //e.printStackTrace( System.err );
334 continue;
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 ) )
381 t[ 0 ] = x;
382 t[ 1 ] = y;
385 ai.applyInverseInPlace( t );
387 catch ( final Exception e )
389 //e.printStackTrace( System.err );
390 continue;
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 ) )
436 t[ 0 ] = x;
437 t[ 1 ] = y;
440 ai.applyInverseInPlace( t );
442 catch ( final Exception e )
444 //e.printStackTrace( System.err );
445 continue;
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 );
470 threads.add( mtt );
471 mtt.start();
473 for ( final Thread mtt : threads )
477 mtt.join();
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 );
490 else
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 );
516 threads.add( mtt );
517 mtt.start();
519 for ( final Thread mtt : threads )
523 mtt.join();
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 );
536 else
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.
565 * @param source
566 * @param alpha
567 * @param target
568 * @param numThreads
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 );
583 threads.add( mtt );
584 mtt.start();
586 for ( final Thread mtt : threads )
590 mtt.join();
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.
602 * @param source
603 * @param alpha
604 * @param target
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() );