diff --git a/src/main/java/qupath/ext/imglib2/AccessibleScaler.java b/src/main/java/qupath/ext/imglib2/AccessibleScaler.java index d33280a..f2d45b9 100644 --- a/src/main/java/qupath/ext/imglib2/AccessibleScaler.java +++ b/src/main/java/qupath/ext/imglib2/AccessibleScaler.java @@ -9,7 +9,6 @@ import net.imglib2.realtransform.RealViews; import net.imglib2.realtransform.Scale2D; import net.imglib2.realtransform.Translation2D; -import net.imglib2.type.NativeType; import net.imglib2.type.numeric.NumericType; import net.imglib2.view.Views; @@ -40,7 +39,7 @@ private AccessibleScaler() { * @throws IllegalArgumentException if the input interval has at least one minimum different from 0, if the provided scale is less * than or equal to 0, or if the input interval has less than two dimensions */ - public static & NumericType> RandomAccessibleInterval scaleWithLinearInterpolation( + public static > RandomAccessibleInterval scaleWithLinearInterpolation( RandomAccessibleInterval input, double scale ) { @@ -59,7 +58,7 @@ public static & NumericType> RandomAccessibleInterva * @throws IllegalArgumentException if the input interval has at least one minimum different from 0, if the provided scale is less * than or equal to 0, or if the input interval has less than two dimensions */ - public static & NumericType> RandomAccessibleInterval scaleWithNearestNeighborInterpolation( + public static > RandomAccessibleInterval scaleWithNearestNeighborInterpolation( RandomAccessibleInterval input, double scale ) { @@ -79,7 +78,7 @@ public static & NumericType> RandomAccessibleInterva * @throws IllegalArgumentException if the input interval has at least one minimum different from 0, if the provided scale is less * than or equal to 0, or if the input interval has less than two dimensions */ - public static & NumericType> RandomAccessibleInterval scale( + public static > RandomAccessibleInterval scale( RandomAccessibleInterval input, double scale, InterpolatorFactory> interpolatorFactory @@ -104,7 +103,7 @@ public static & NumericType> RandomAccessibleInterva } } - private static & NumericType> RandomAccessibleInterval scaleWithoutChecks( + private static > RandomAccessibleInterval scaleWithoutChecks( RandomAccessibleInterval input, double scale, Scale2D scale2D, diff --git a/src/main/java/qupath/ext/imglib2/ImgCreator.java b/src/main/java/qupath/ext/imglib2/ImgCreator.java index ddf8dc8..600556d 100644 --- a/src/main/java/qupath/ext/imglib2/ImgCreator.java +++ b/src/main/java/qupath/ext/imglib2/ImgCreator.java @@ -26,7 +26,6 @@ import qupath.lib.images.servers.ImageServerMetadata; import qupath.lib.images.servers.ServerTools; import qupath.lib.images.servers.TileRequest; -import qupath.lib.images.servers.PixelType; import java.awt.image.BufferedImage; import java.io.IOException; @@ -38,14 +37,13 @@ /** * A class to create {@link Img} or {@link RandomAccessibleInterval} from an {@link ImageServer}. *

- * Use a {@link #builder(ImageServer)} or {@link #builder(ImageServer, NativeType)} to create an instance of this class. + * Use a {@link #builder(ImageServer)} or {@link #builder(ImageServer)} to create an instance of this class. *

* This class is thread-safe. * - * @param the type of the returned accessibles * @param the type contained in the input image */ -public class ImgCreator & NumericType, A extends SizableDataAccess> { +public class ImgCreator { /** * The index of the X axis of accessibles returned by functions of this class @@ -72,14 +70,12 @@ public class ImgCreator & NumericType, A extends Siza */ public static final int NUMBER_OF_AXES = 5; private final ImageServer server; - private final T type; private final CellCache cellCache; private final Function cellCreator; private final int numberOfChannels; - private ImgCreator(Builder builder, Function cellCreator) { + private ImgCreator(Builder builder, Function cellCreator) { this.server = builder.server; - this.type = builder.type; this.cellCache = builder.cellCache; this.cellCreator = cellCreator; this.numberOfChannels = server.isRGB() ? 1 : server.nChannels(); @@ -87,74 +83,14 @@ private ImgCreator(Builder builder, Function cellCreator) { /** * Create a builder from an {@link ImageServer}. This doesn't create any accessibles yet. - *

- * The type of the output image is not checked, which might lead to problems later when accessing pixel values of the - * returned accessibles of this class. It is recommended to use {@link #builder(ImageServer, NativeType)} instead. * * @param server the input image * @return a builder to create an instance of this class * @throws IllegalArgumentException if the provided image has less than one channel - * @param the type of the output image */ - public static & NumericType> Builder builder(ImageServer server) { + public static Builder builder(ImageServer server) { // Despite the potential warning, T is necessary, otherwise a cannot infer type arguments error occurs - return new Builder(server, getTypeOfServer(server)); - } - - /** - * Create a builder from an {@link ImageServer}. This doesn't create any accessibles yet. - *

- * The provided type must be compatible with the input image: - *

- * - * @param server the input image - * @param type the expected type of the output image - * @return a builder to create an instance of this class - * @param the type corresponding to the provided image - * @throws IllegalArgumentException if the provided type is not compatible with the input image (see above), or if the provided image - * has less than one channel - */ - public static & NumericType> Builder builder(ImageServer server, T type) { - return new Builder<>(server, type); + return new Builder(server); } /** @@ -171,8 +107,9 @@ public static & NumericType> Builder builder(Imag * @param level the level to consider * @return an {@link Img} corresponding to the provided level of the input image * @throws IllegalArgumentException if the provided level does not match with a level of the input image + * @param Generic parameter for the image type. */ - public Img createForLevel(int level) { + public & NativeType> Img createForLevel(int level) { if (level < 0 || level >= server.getMetadata().nLevels()) { throw new IllegalArgumentException(String.format( "The provided level %d is not within 0 and %d", @@ -183,16 +120,17 @@ public Img createForLevel(int level) { List tiles = new ArrayList<>(server.getTileRequestManager().getTileRequestsForLevel(level)); + T type = getTypeOfServer(server); return new LazyCellImg<>( new CellGrid( - new long[] { + new long[]{ server.getMetadata().getLevel(level).getWidth(), server.getMetadata().getLevel(level).getHeight(), numberOfChannels, server.nZSlices(), server.nTimepoints() }, - new int[] { + new int[]{ server.getMetadata().getPreferredTileWidth(), server.getMetadata().getPreferredTileHeight(), numberOfChannels, @@ -223,41 +161,37 @@ public Img createForLevel(int level) { * @param downsample the downsample to apply to the input image. Must be greater than 0 * @return a {@link RandomAccessibleInterval} corresponding to the input image with the provided downsample applied * @throws IllegalArgumentException if the provided downsample is not greater than 0 + * @param Generic parameter for the image type. */ - public RandomAccessibleInterval createForDownsample(double downsample) { + public & NativeType> RandomAccessibleInterval createForDownsample(double downsample) { if (downsample <= 0) { throw new IllegalArgumentException(String.format("The provided downsample %f is not greater than 0", downsample)); } int level = ServerTools.getPreferredResolutionLevel(server, downsample); + Img img = createForLevel(level); if (server.getMetadata().getChannelType() == ImageServerMetadata.ChannelType.CLASSIFICATION) { - return AccessibleScaler.scaleWithNearestNeighborInterpolation(createForLevel(level), server.getDownsampleForResolution(level) / downsample); + return AccessibleScaler.scaleWithNearestNeighborInterpolation(img, server.getDownsampleForResolution(level) / downsample); } else { - return AccessibleScaler.scaleWithLinearInterpolation(createForLevel(level), server.getDownsampleForResolution(level) / downsample); + return AccessibleScaler.scaleWithLinearInterpolation(img, server.getDownsampleForResolution(level) / downsample); } } /** * A builder to create an instance of {@link ImgCreator}. - * - * @param the type of the returned accessibles of {@link ImgCreator} should have */ - public static class Builder & NumericType> { + public static class Builder { private static final CellCache defaultCellCache = new CellCache((int) (Runtime.getRuntime().maxMemory() * 0.5 / (1024 * 1024))); private final ImageServer server; - private final T type; private CellCache cellCache = defaultCellCache; - private Builder(ImageServer server, T type) { - checkType(server, type); + private Builder(ImageServer server) { if (server.nChannels() <= 0) { throw new IllegalArgumentException(String.format("The provided image has less than one channel (%d)", server.nChannels())); } - this.server = server; - this.type = type; } /** @@ -268,7 +202,7 @@ private Builder(ImageServer server, T type) { * @return this builder * @throws NullPointerException if the provided cache is null */ - public Builder cellCache(CellCache cellCache) { + public Builder cellCache(CellCache cellCache) { this.cellCache = Objects.requireNonNull(cellCache); return this; } @@ -278,7 +212,7 @@ public Builder cellCache(CellCache cellCache) { * * @return a new instance of {@link ImgCreator} */ - public ImgCreator build() { + public ImgCreator build() { if (server.isRGB()) { return new ImgCreator<>(this, ArgbBufferedImageAccess::new); } else { @@ -291,88 +225,11 @@ public Builder cellCache(CellCache cellCache) { }; } } - - private static void checkType(ImageServer server, T type) { - if (server.isRGB()) { - if (!(type instanceof ARGBType)) { - throw new IllegalArgumentException(String.format( - "The provided type %s is not an ARGBType, which is the one expected for RGB images", - type - )); - } - } else { - switch (server.getPixelType()) { - case UINT8 -> { - if (!(type instanceof UnsignedByteType)) { - throw new IllegalArgumentException(String.format( - "The provided type %s is not a ByteType, which is the one expected for non-RGB UINT8 images", - type - )); - } - } - case INT8 -> { - if (!(type instanceof ByteType)) { - throw new IllegalArgumentException(String.format( - "The provided type %s is not a UnsignedByteType, which is the one expected for non-RGB INT8 images", - type - )); - } - } - case UINT16 -> { - if (!(type instanceof UnsignedShortType)) { - throw new IllegalArgumentException(String.format( - "The provided type %s is not a UnsignedShortType, which is the one expected for non-RGB UINT16 images", - type - )); - } - } - case INT16 -> { - if (!(type instanceof ShortType)) { - throw new IllegalArgumentException(String.format( - "The provided type %s is not a ShortType, which is the one expected for non-RGB INT16 images", - type - )); - } - } - case UINT32 -> { - if (!(type instanceof UnsignedIntType)) { - throw new IllegalArgumentException(String.format( - "The provided type %s is not a UnsignedIntType, which is the one expected for non-RGB UINT32 images", - type - )); - } - } - case INT32 -> { - if (!(type instanceof IntType)) { - throw new IllegalArgumentException(String.format( - "The provided type %s is not a IntType, which is the one expected for non-RGB INT32 images", - type - )); - } - } - case FLOAT32 -> { - if (!(type instanceof FloatType)) { - throw new IllegalArgumentException(String.format( - "The provided type %s is not a FloatType, which is the one expected for non-RGB FLOAT32 images", - type - )); - } - } - case FLOAT64 -> { - if (!(type instanceof DoubleType)) { - throw new IllegalArgumentException(String.format( - "The provided type %s is not a DoubleType, which is the one expected for non-RGB FLOAT64 images", - type - )); - } - } - } - } - } + } @SuppressWarnings("unchecked") - private static & NumericType> T getTypeOfServer(ImageServer server) { + private static & NativeType> T getTypeOfServer(ImageServer server) { if (server.isRGB()) { return (T) new ARGBType(); } diff --git a/src/main/java/qupath/ext/imglib2/ImgLib2ImageServer.java b/src/main/java/qupath/ext/imglib2/ImgLib2ImageServer.java index 9dcd295..3bdda9d 100644 --- a/src/main/java/qupath/ext/imglib2/ImgLib2ImageServer.java +++ b/src/main/java/qupath/ext/imglib2/ImgLib2ImageServer.java @@ -96,6 +96,8 @@ private ImgLib2ImageServer(List> accessibl * if the provided accessibles do not have {@link ImgCreator#NUMBER_OF_AXES} axes, if the provided accessibles * do not have the same number of channels, z-stacks, or timepoints, or if the accessible type is {@link ARGBType} * and the number of channels of the accessibles is not 1 + * @return the builder + * @param Generic parameter for the image type. */ public static & NumericType> Builder builder(List> accessibles) { return new Builder<>(accessibles); diff --git a/src/test/java/qupath/ext/imglib2/TestImgCreator.java b/src/test/java/qupath/ext/imglib2/TestImgCreator.java index 6cfe5cc..7fa68ca 100644 --- a/src/test/java/qupath/ext/imglib2/TestImgCreator.java +++ b/src/test/java/qupath/ext/imglib2/TestImgCreator.java @@ -44,7 +44,7 @@ void Check_Rgb_Server() throws Exception { PixelType pixelType = PixelType.UINT8; ImageServer imageServer = new GenericImageServer(isRgb, pixelType); - Img img = ImgCreator.builder(imageServer, new ARGBType()).build().createForLevel(0); + Img img = ImgCreator.builder(imageServer).build().createForLevel(0); Utils.assertArgbRandomAccessibleEquals(img, (x, y, channel, z, t) -> ARGBType.rgba(255, 0, 0, 0), 1); @@ -57,7 +57,7 @@ void Check_Uint8_Server() throws Exception { PixelType pixelType = PixelType.UINT8; ImageServer imageServer = new GenericImageServer(isRgb, pixelType); - Img img = ImgCreator.builder(imageServer, new UnsignedByteType()).build().createForLevel(0); + Img img = ImgCreator.builder(imageServer).build().createForLevel(0); Utils.assertRandomAccessibleEquals(img, (x, y, channel, z, t) -> 1, 1); @@ -70,7 +70,7 @@ void Check_Int8_Server() throws Exception { PixelType pixelType = PixelType.INT8; ImageServer imageServer = new GenericImageServer(isRgb, pixelType); - Img img = ImgCreator.builder(imageServer, new ByteType()).build().createForLevel(0); + Img img = ImgCreator.builder(imageServer).build().createForLevel(0); Utils.assertRandomAccessibleEquals(img, (x, y, channel, z, t) -> 1, 1); @@ -83,7 +83,7 @@ void Check_Uint16_Server() throws Exception { PixelType pixelType = PixelType.UINT16; ImageServer imageServer = new GenericImageServer(isRgb, pixelType); - Img img = ImgCreator.builder(imageServer, new UnsignedShortType()).build().createForLevel(0); + Img img = ImgCreator.builder(imageServer).build().createForLevel(0); Utils.assertRandomAccessibleEquals(img, (x, y, channel, z, t) -> 1, 1); @@ -96,7 +96,7 @@ void Check_Int16_Server() throws Exception { PixelType pixelType = PixelType.INT16; ImageServer imageServer = new GenericImageServer(isRgb, pixelType); - Img img = ImgCreator.builder(imageServer, new ShortType()).build().createForLevel(0); + Img img = ImgCreator.builder(imageServer).build().createForLevel(0); Utils.assertRandomAccessibleEquals(img, (x, y, channel, z, t) -> 1, 1); @@ -109,7 +109,7 @@ void Check_Uint32_Server() throws Exception { PixelType pixelType = PixelType.UINT32; ImageServer imageServer = new GenericImageServer(isRgb, pixelType); - Img img = ImgCreator.builder(imageServer, new UnsignedIntType()).build().createForLevel(0); + Img img = ImgCreator.builder(imageServer).build().createForLevel(0); Utils.assertRandomAccessibleEquals(img, (x, y, channel, z, t) -> 1, 1); @@ -122,7 +122,7 @@ void Check_Int32_Server() throws Exception { PixelType pixelType = PixelType.INT32; ImageServer imageServer = new GenericImageServer(isRgb, pixelType); - Img img = ImgCreator.builder(imageServer, new IntType()).build().createForLevel(0); + Img img = ImgCreator.builder(imageServer).build().createForLevel(0); Utils.assertRandomAccessibleEquals(img, (x, y, channel, z, t) -> 1, 1); @@ -135,7 +135,7 @@ void Check_Float32_Server() throws Exception { PixelType pixelType = PixelType.FLOAT32; ImageServer imageServer = new GenericImageServer(isRgb, pixelType); - Img img = ImgCreator.builder(imageServer, new FloatType()).build().createForLevel(0); + Img img = ImgCreator.builder(imageServer).build().createForLevel(0); Utils.assertRandomAccessibleEquals(img, (x, y, channel, z, t) -> 1, 1); @@ -148,7 +148,7 @@ void Check_Float64_Server() throws Exception { PixelType pixelType = PixelType.FLOAT64; ImageServer imageServer = new GenericImageServer(isRgb, pixelType); - Img img = ImgCreator.builder(imageServer, new DoubleType()).build().createForLevel(0); + Img img = ImgCreator.builder(imageServer).build().createForLevel(0); Utils.assertRandomAccessibleEquals(img, (x, y, channel, z, t) -> 1, 1); @@ -221,7 +221,7 @@ void Check_Pixels_Of_Level_0() throws Exception { ImageServer imageServer = new ComplexDoubleImageServer(); double downsample = imageServer.getDownsampleForResolution(level); - Img img = ImgCreator.builder(imageServer, new DoubleType()).build().createForLevel(level); + Img img = ImgCreator.builder(imageServer).build().createForLevel(level); Utils.assertRandomAccessibleEquals(img, ComplexDoubleImageServer::getPixel, downsample); @@ -234,7 +234,7 @@ void Check_Pixels_Of_Level_1() throws Exception { ImageServer imageServer = new ComplexDoubleImageServer(); double downsample = imageServer.getDownsampleForResolution(level); - Img img = ImgCreator.builder(imageServer, new DoubleType()).build().createForLevel(level); + Img img = ImgCreator.builder(imageServer).build().createForLevel(level); Utils.assertRandomAccessibleEquals(img, ComplexDoubleImageServer::getPixel, downsample); @@ -247,7 +247,7 @@ void Check_Pixels_Of_Downsample_1() throws Exception { ImageServer imageServer = new ComplexDoubleImageServer(); double downsample = imageServer.getDownsampleForResolution(level); - RandomAccessibleInterval img = ImgCreator.builder(imageServer, new DoubleType()).build().createForDownsample(downsample); + RandomAccessibleInterval img = ImgCreator.builder(imageServer).build().createForDownsample(downsample); Utils.assertRandomAccessibleEquals(img, ComplexDoubleImageServer::getPixel, downsample); @@ -260,7 +260,7 @@ void Check_Pixels_Of_Downsample_4() throws Exception { ImageServer imageServer = new ComplexDoubleImageServer(); double downsample = imageServer.getDownsampleForResolution(level); - RandomAccessibleInterval img = ImgCreator.builder(imageServer, new DoubleType()).build().createForDownsample(downsample); + RandomAccessibleInterval img = ImgCreator.builder(imageServer).build().createForDownsample(downsample); Utils.assertRandomAccessibleEquals(img, ComplexDoubleImageServer::getPixel, downsample);