|
39 | 39 | import net.imglib2.algorithm.blocks.BlockSupplier;
|
40 | 40 | import net.imglib2.algorithm.blocks.ClampType;
|
41 | 41 | import net.imglib2.algorithm.blocks.ComputationType;
|
| 42 | +import net.imglib2.algorithm.blocks.downsample.DownsampleBlockProcessors.AvgBlockDouble; |
| 43 | +import net.imglib2.algorithm.blocks.downsample.DownsampleBlockProcessors.AvgBlockFloat; |
42 | 44 | import net.imglib2.algorithm.blocks.downsample.DownsampleBlockProcessors.CenterDouble;
|
43 | 45 | import net.imglib2.algorithm.blocks.downsample.DownsampleBlockProcessors.CenterFloat;
|
44 | 46 | import net.imglib2.algorithm.blocks.downsample.DownsampleBlockProcessors.HalfPixelDouble;
|
|
103 | 105 | * pixels are centered on input pixels.
|
104 | 106 | * </li>
|
105 | 107 | * </ul>
|
106 |
| - * |
| 108 | + * <p> |
| 109 | + * The {@link #downsample(ComputationType, int[])} methods support downsampling |
| 110 | + * with arbitrary integer factor by averaging blocks of pixels. (With {@code |
| 111 | + * factors={2,2,2,...}}, this is equivalent to downsampling with {@link |
| 112 | + * Offset#HALF_PIXEL}). |
107 | 113 | */
|
108 | 114 | public class Downsample
|
109 | 115 | {
|
@@ -152,6 +158,34 @@ public static long[] getDownsampledDimensions( final long[] imgDimensions, final
|
152 | 158 | return destSize;
|
153 | 159 | }
|
154 | 160 |
|
| 161 | + /** |
| 162 | + * Returns recommended size of the downsampled image, given an input image |
| 163 | + * of size {@code imgDimensions}. In each dimension, the recommended size is |
| 164 | + * input {@code imgDimension} / {@code downsamplingFactor}, rounding up |
| 165 | + * (e.g., a 5 pixel input image downsampled with factor 2, should yield a 3 |
| 166 | + * pixel image). |
| 167 | + * |
| 168 | + * @param imgDimensions |
| 169 | + * dimensions of the input image |
| 170 | + * @param downsamplingFactors |
| 171 | + * in each dimension {@code d}, {@code downsamplingFactors[d]} |
| 172 | + * pixels in the input image should be averaged to one output pixel. |
| 173 | + * {@code downsamplingFactors} is expanded or truncated to the |
| 174 | + * necessary size. For example, if {@code downsamplingFactors=={2,2,1}} and |
| 175 | + * the operator is applied to a 2D image, {@code downsamplingFactors} is |
| 176 | + * truncated to {@code {2, 2}}. If the operator is applied to a 5D image, |
| 177 | + * {@code downsamplingFactors} is expanded to {@code {2, 2, 1, 1, 1}} |
| 178 | + * |
| 179 | + * @return the recommended size of the downsampled image |
| 180 | + */ |
| 181 | + public static long[] getDownsampledDimensions( final long[] imgDimensions, final int[] downsamplingFactors ) |
| 182 | + { |
| 183 | + final int[] dFactorsX = Util.expandArray( downsamplingFactors, imgDimensions.length ); |
| 184 | + final long[] destSize = new long[ imgDimensions.length ]; |
| 185 | + Arrays.setAll( destSize, d -> ( imgDimensions[ d ] + dFactorsX[ d ] - 1 ) / dFactorsX[ d ] ); |
| 186 | + return destSize; |
| 187 | + } |
| 188 | + |
155 | 189 | /**
|
156 | 190 | * Specify where downsampled pixels should be placed.
|
157 | 191 | */
|
@@ -335,6 +369,82 @@ Function< BlockSupplier< T >, UnaryBlockOperator< T, T > > downsample( final Com
|
335 | 369 | };
|
336 | 370 | }
|
337 | 371 |
|
| 372 | + /** |
| 373 | + * Downsample (by the given {@code downsamplingFactors}) blocks of the |
| 374 | + * standard ImgLib2 {@code RealType}s. |
| 375 | + * <p> |
| 376 | + * Supported types are {@code UnsignedByteType}, {@code UnsignedShortType}, |
| 377 | + * {@code UnsignedIntType}, {@code ByteType}, {@code ShortType}, {@code |
| 378 | + * IntType}, {@code LongType}, {@code FloatType}, {@code DoubleType}). |
| 379 | + * <p> |
| 380 | + * Precision for intermediate values is chosen as to represent the |
| 381 | + * input/output type without loss of precision. That is, {@code FLOAT} for |
| 382 | + * u8, i8, u16, i16, i32, f32, and otherwise {@code DOUBLE} for u32, i64, |
| 383 | + * f64. |
| 384 | + * <p> |
| 385 | + * The returned factory function creates an operator matching the |
| 386 | + * type and dimensionality of a given input {@code BlockSupplier<T>}. |
| 387 | + * |
| 388 | + * @param downsamplingFactors |
| 389 | + * in each dimension {@code d}, {@code downsamplingFactors[d]} |
| 390 | + * pixels in the input image should be averaged to one output pixel. |
| 391 | + * {@code downsamplingFactors} is expanded or truncated to the |
| 392 | + * necessary size. For example, if {@code downsamplingFactors=={2,2,1}} and |
| 393 | + * the operator is applied to a 2D image, {@code downsamplingFactors} is |
| 394 | + * truncated to {@code {2, 2}}. If the operator is applied to a 5D image, |
| 395 | + * {@code downsamplingFactors} is expanded to {@code {2, 2, 1, 1, 1}} |
| 396 | + * @param <T> |
| 397 | + * the input/output type |
| 398 | + * |
| 399 | + * @return factory for {@code UnaryBlockOperator} to downsample blocks of type {@code T} |
| 400 | + */ |
| 401 | + public static < T extends NativeType< T > > |
| 402 | + Function< BlockSupplier< T >, UnaryBlockOperator< T, T > > downsample( final int[] downsamplingFactors ) |
| 403 | + { |
| 404 | + return downsample( ComputationType.AUTO, downsamplingFactors ); |
| 405 | + } |
| 406 | + |
| 407 | + /** |
| 408 | + * Downsample (by the given {@code downsamplingFactors}) blocks of the |
| 409 | + * standard ImgLib2 {@code RealType}s. |
| 410 | + * <p> |
| 411 | + * Supported types are {@code UnsignedByteType}, {@code UnsignedShortType}, |
| 412 | + * {@code UnsignedIntType}, {@code ByteType}, {@code ShortType}, {@code |
| 413 | + * IntType}, {@code LongType}, {@code FloatType}, {@code DoubleType}). |
| 414 | + * <p> |
| 415 | + * The returned factory function creates an operator matching the |
| 416 | + * type and dimensionality of a given input {@code BlockSupplier<T>}. |
| 417 | + * |
| 418 | + * @param computationType |
| 419 | + * specifies in which precision intermediate values should be |
| 420 | + * computed. For {@code AUTO}, the type that can represent the |
| 421 | + * input/output type without loss of precision is picked. That is, |
| 422 | + * {@code FLOAT} for u8, i8, u16, i16, i32, f32, and otherwise {@code |
| 423 | + * DOUBLE} for u32, i64, f64. |
| 424 | + * @param downsamplingFactors |
| 425 | + * in each dimension {@code d}, {@code downsamplingFactors[d]} |
| 426 | + * pixels in the input image should be averaged to one output pixel. |
| 427 | + * {@code downsamplingFactors} is expanded or truncated to the |
| 428 | + * necessary size. For example, if {@code downsamplingFactors=={2,2,1}} and |
| 429 | + * the operator is applied to a 2D image, {@code downsamplingFactors} is |
| 430 | + * truncated to {@code {2, 2}}. If the operator is applied to a 5D image, |
| 431 | + * {@code downsamplingFactors} is expanded to {@code {2, 2, 1, 1, 1}} |
| 432 | + * @param <T> |
| 433 | + * the input/output type |
| 434 | + * |
| 435 | + * @return factory for {@code UnaryBlockOperator} to downsample blocks of type {@code T} |
| 436 | + */ |
| 437 | + public static < T extends NativeType< T > > |
| 438 | + Function< BlockSupplier< T >, UnaryBlockOperator< T, T > > downsample( final ComputationType computationType, final int[] downsamplingFactors ) |
| 439 | + { |
| 440 | + return s -> { |
| 441 | + final T type = s.getType(); |
| 442 | + final int n = s.numDimensions(); |
| 443 | + final int[] expandedDownsamplingFactors = Util.expandArray( downsamplingFactors, n ); |
| 444 | + return createOperator( type, computationType, expandedDownsamplingFactors ); |
| 445 | + }; |
| 446 | + } |
| 447 | + |
338 | 448 | /**
|
339 | 449 | * Create a {@code UnaryBlockOperator} to downsample (by factor 2) blocks of
|
340 | 450 | * the standard ImgLib2 {@code RealType}. The {@code downsampleInDim}
|
@@ -373,25 +483,45 @@ Function< BlockSupplier< T >, UnaryBlockOperator< T, T > > downsample( final Com
|
373 | 483 | public static < T extends NativeType< T > >
|
374 | 484 | UnaryBlockOperator< T, T > createOperator( final T type, final ComputationType computationType, final Offset offset, final boolean[] downsampleInDim )
|
375 | 485 | {
|
376 |
| - final boolean processAsFloat; |
| 486 | + final UnaryBlockOperator< ?, ? > op = processAsFloat( computationType, type ) |
| 487 | + ? downsampleFloat( offset, downsampleInDim ) |
| 488 | + : downsampleDouble( offset, downsampleInDim ); |
| 489 | + return op.adaptSourceType( type, ClampType.NONE ).adaptTargetType( type, ClampType.NONE ); |
| 490 | + } |
| 491 | + |
| 492 | + private static < T extends NativeType< T > > boolean processAsFloat( final ComputationType computationType, final T type ) |
| 493 | + { |
377 | 494 | switch ( computationType )
|
378 | 495 | {
|
379 | 496 | case FLOAT:
|
380 |
| - processAsFloat = true; |
381 |
| - break; |
| 497 | + return true; |
382 | 498 | case DOUBLE:
|
383 |
| - processAsFloat = false; |
384 |
| - break; |
385 |
| - default: |
| 499 | + return true; |
386 | 500 | case AUTO:
|
| 501 | + default: |
387 | 502 | final PrimitiveType pt = type.getNativeTypeFactory().getPrimitiveType();
|
388 |
| - processAsFloat = pt.equals( FLOAT ) || pt.getByteCount() < FLOAT.getByteCount(); |
389 |
| - break; |
| 503 | + return pt.equals( FLOAT ) || pt.getByteCount() < FLOAT.getByteCount(); |
390 | 504 | }
|
391 |
| - final UnaryBlockOperator< ?, ? > op = processAsFloat |
392 |
| - ? downsampleFloat( offset, downsampleInDim ) |
393 |
| - : downsampleDouble( offset, downsampleInDim ); |
394 |
| - return op.adaptSourceType( type, ClampType.NONE ).adaptTargetType( type, ClampType.NONE ); |
| 505 | + } |
| 506 | + |
| 507 | + private static UnaryBlockOperator< FloatType, FloatType > downsampleFloat( final Offset offset, final boolean[] downsampleInDim ) |
| 508 | + { |
| 509 | + final FloatType type = new FloatType(); |
| 510 | + final int n = downsampleInDim.length; |
| 511 | + return new DefaultUnaryBlockOperator<>( type, type, n, n, |
| 512 | + offset == Offset.HALF_PIXEL |
| 513 | + ? new HalfPixelFloat( downsampleInDim ) |
| 514 | + : new CenterFloat( downsampleInDim ) ); |
| 515 | + } |
| 516 | + |
| 517 | + private static UnaryBlockOperator< DoubleType, DoubleType > downsampleDouble( final Offset offset, final boolean[] downsampleInDim ) |
| 518 | + { |
| 519 | + final DoubleType type = new DoubleType(); |
| 520 | + final int n = downsampleInDim.length; |
| 521 | + return new DefaultUnaryBlockOperator<>( type, type, n, n, |
| 522 | + offset == Offset.HALF_PIXEL |
| 523 | + ? new HalfPixelDouble( downsampleInDim ) |
| 524 | + : new CenterDouble( downsampleInDim ) ); |
395 | 525 | }
|
396 | 526 |
|
397 | 527 | /**
|
@@ -435,23 +565,51 @@ UnaryBlockOperator< T, T > createOperator( final T type, final ComputationType c
|
435 | 565 | return createOperator( type, computationType, offset, downsampleInDim );
|
436 | 566 | }
|
437 | 567 |
|
438 |
| - private static UnaryBlockOperator< FloatType, FloatType > downsampleFloat( final Offset offset, final boolean[] downsampleInDim ) |
| 568 | + /** |
| 569 | + * Create a {@code UnaryBlockOperator} to downsample (by the given {@code |
| 570 | + * downsamplingFactors}) blocks of the standard ImgLib2 {@code RealType}. |
| 571 | + * <p> |
| 572 | + * Supported types are {@code UnsignedByteType}, {@code UnsignedShortType}, |
| 573 | + * {@code UnsignedIntType}, {@code ByteType}, {@code ShortType}, {@code |
| 574 | + * IntType}, {@code LongType}, {@code FloatType}, {@code DoubleType}). |
| 575 | + * |
| 576 | + * @param type |
| 577 | + * instance of the input type |
| 578 | + * @param computationType |
| 579 | + * specifies in which precision intermediate values should be |
| 580 | + * computed. For {@code AUTO}, the type that can represent the |
| 581 | + * input/output type without loss of precision is picked. That is, |
| 582 | + * {@code FLOAT} for u8, i8, u16, i16, i32, f32, and otherwise {@code |
| 583 | + * DOUBLE} for u32, i64, f64. |
| 584 | + * @param downsamplingFactors |
| 585 | + * in each dimension {@code d}, {@code downsamplingFactors[d]} |
| 586 | + * pixels in the input image should be averaged to one output pixel. |
| 587 | + * @param <T> |
| 588 | + * the input/output type |
| 589 | + * |
| 590 | + * @return {@code UnaryBlockOperator} to downsample blocks of type {@code T} |
| 591 | + */ |
| 592 | + public static < T extends NativeType< T > > |
| 593 | + UnaryBlockOperator< T, T > createOperator( final T type, final ComputationType computationType, final int[] downsamplingFactors ) |
| 594 | + { |
| 595 | + final UnaryBlockOperator< ?, ? > op = processAsFloat( computationType, type ) |
| 596 | + ? downsampleFloat( downsamplingFactors ) |
| 597 | + : downsampleDouble( downsamplingFactors ); |
| 598 | + return op.adaptSourceType( type, ClampType.NONE ).adaptTargetType( type, ClampType.NONE ); |
| 599 | + } |
| 600 | + |
| 601 | + private static UnaryBlockOperator< FloatType, FloatType > downsampleFloat( final int[] downsamplingFactors ) |
439 | 602 | {
|
440 | 603 | final FloatType type = new FloatType();
|
441 |
| - final int n = downsampleInDim.length; |
442 |
| - return new DefaultUnaryBlockOperator<>( type, type, n, n, |
443 |
| - offset == Offset.HALF_PIXEL |
444 |
| - ? new HalfPixelFloat( downsampleInDim ) |
445 |
| - : new CenterFloat( downsampleInDim ) ); |
| 604 | + final int n = downsamplingFactors.length; |
| 605 | + return new DefaultUnaryBlockOperator<>( type, type, n, n, new AvgBlockFloat( downsamplingFactors ) ); |
446 | 606 | }
|
447 | 607 |
|
448 |
| - private static UnaryBlockOperator< DoubleType, DoubleType > downsampleDouble( final Offset offset, final boolean[] downsampleInDim ) |
| 608 | + private static UnaryBlockOperator< DoubleType, DoubleType > downsampleDouble( final int[] downsamplingFactors ) |
449 | 609 | {
|
450 | 610 | final DoubleType type = new DoubleType();
|
451 |
| - final int n = downsampleInDim.length; |
452 |
| - return new DefaultUnaryBlockOperator<>( type, type, n, n, |
453 |
| - offset == Offset.HALF_PIXEL |
454 |
| - ? new HalfPixelDouble( downsampleInDim ) |
455 |
| - : new CenterDouble( downsampleInDim ) ); |
| 611 | + final int n = downsamplingFactors.length; |
| 612 | + return new DefaultUnaryBlockOperator<>( type, type, n, n, new AvgBlockDouble( downsamplingFactors ) ); |
456 | 613 | }
|
| 614 | + |
457 | 615 | }
|
0 commit comments