-
Notifications
You must be signed in to change notification settings - Fork 1
Trying to simplify #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
I confess to being quite lost at this point so don't feel very confident contributing. Is there code with a similar purpose to compare with somewhere in the imglib2/BDV/etc ecosystem? Or is it worth reaching out for some sanity-checking? |
|
The closest thing I can think of it is in It looks like it isn't easy to handle the fact that you might not know what the type of the image you want is. |
|
This is how I understand the issue... we want to be able to write ImgLib2 code and the challenge is to get our inputs in a convenient way. I've given 3 examples below. // Define ImgLib2 function with whatever types are needed - the challenge is to figure out how to get a suitable input.
public static <T extends NumericType<T> & NativeType<T>> void smoothAndShow(RandomAccessibleInterval<T> imgOrig) {
// NumericType needed for smoothing, NativeType needed to create an ArrayImg (which we assume is small enough for memory)
RandomAccessibleInterval<T> img2D = Views.hyperSlice(Views.dropSingletonDimensions(imgOrig), 2, 0);
ArrayImg<T, ?> imgSmooth = new ArrayImgFactory<>(imgOrig.getType()).create(img2D.dimensionsAsLongArray());
Gauss3.gauss(10.0, Views.extendMirrorSingle(img2D), imgSmooth);
ImageJFunctions.show(imgSmooth);
}
// Option 1: Try to use generics, even when type not known
public static <T extends RealType<T> & NativeType<T>> void smoothAndShow(ImageServer<BufferedImage> server) {
T type = ImgBuilder.getRealType(server.getPixelType());
RandomAccessibleInterval<T> imgOrig = ImgBuilder.createRealBuilder(server, type).buildForLevel(server.nResolutions()-1);
smoothAndShow(imgOrig);
}
// Option 2: Use raw types to try to smuggle image through
public static void smoothAndShowNonGeneric(ImageServer<BufferedImage> server) {
var imgOrig = ImgBuilder.createRealBuilder(server).buildForLevel(server.nResolutions()-1);
smoothAndShow((RandomAccessibleInterval)imgOrig);
}
// Option 3: Explicitly handle types
public static void smoothAndShowAwkward(ImageServer<BufferedImage> server) {
int level = server.nResolutions()-1;
switch (server.getPixelType()) {
case UINT8 -> {
var imgOrig = ImgBuilder.createBuilder(server, new UnsignedByteType()).buildForLevel(level);
smoothAndShow(imgOrig);
}
case INT8 -> {
var imgOrig = ImgBuilder.createBuilder(server, new ByteType()).buildForLevel(level);
smoothAndShow(imgOrig);
}
case UINT16 -> {
var imgOrig = ImgBuilder.createBuilder(server, new UnsignedShortType()).buildForLevel(level);
smoothAndShow(imgOrig);
}
case INT16 -> {
var imgOrig = ImgBuilder.createBuilder(server, new ShortType()).buildForLevel(level);
smoothAndShow(imgOrig);
}
case UINT32 -> {
var imgOrig = ImgBuilder.createBuilder(server, new UnsignedIntType()).buildForLevel(level);
smoothAndShow(imgOrig);
}
case INT32 -> {
var imgOrig = ImgBuilder.createBuilder(server, new IntType()).buildForLevel(level);
smoothAndShow(imgOrig);
}
case FLOAT32 -> {
var imgOrig = ImgBuilder.createBuilder(server, new FloatType()).buildForLevel(level);
smoothAndShow(imgOrig);
}
case FLOAT64 -> {
var imgOrig = ImgBuilder.createBuilder(server, new DoubleType()).buildForLevel(level);
smoothAndShow(imgOrig);
}
}
} |
|
The scifio code doesn't look a million miles from what we've got here. As long as we're fairly sure that exceptions will be (fairly) local to the code that causes them, then I'm mostly happy (but still very lost). Re: FloatType tf = getRealType(server.getPixelType());I guess we just chalk this up to user error? The nested generic types (which still do not really make sense to me) seem to make some level of unchecked type use inevitable; I certainly recall somebody advising us to just use |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the decrease in number of lines of code and the simplification of the public API compensates the overall use of unsafe functions. I just have a few comments that to simplify even more
|
Thanks @Rylern and @alanocallaghan I think I've fixed/replied to everything. I made some other changes to try to improve reuse/limit potential code paths, for example by restricting calls to the |
Rylern
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor things
This builds on top of #16 with the goal of trying to reduce boilerplate and make it easier to code for not knowing the image type - which is the 'usual' case in QuPath.
Some changes:
AccessibleScalerare very generic - so don't restrict their inputs so much (e.g. don't need aNativeType, and sometimes evenNumericType)ImgBuilder<T,A>- so simplify it toImgBuilder<T>switchstatementscreateRgbBuilderto go along withcreateRealBuilderThe last is to support this kind of code without the user having to know that
ARGBTypeis what they need:Then there's another change that I think will be more controversial:
public static <T extends RealType<T>> T getRealType(PixelType pixelType)This is another generic method where the generic is only in the return value - so I documented why it isn't a good idea.
Still, the point of having it is to support this kind of code where the type is defined for the method:
I'm not sure this was possible with #16 because returning
ImgBuilder<? extends NumericType<?>, ?>when the type wasn't known seemed too aggressive in throwing away information. Unless there was a safer syntax I missed, I think it pushes people towards using raw types or really large switch statements - both of which rather go against the point of Java generics and ImgLib2's purpose.A big disadvantage is that it's possible to do something like this
however an exception should be thrown as soon as the type is used to create an image with an incompatible server, which I think mitigates the risk. At least it seems less dangerous than my previous attempts, which could allow this kind of thing
and have the error show up much later.
The ultimate goal was to try to figure out some safer syntax that was also intuitive. This didn't really work out.
The method below is some code that I added at the end of
ImgBuilder, which can be used to explore my attempts to see what did and didn't work (spoiler: most things don't work).I used
Views.collapseRealbecause it has this signature:and I wanted to see could be go from any arbitrary
ImageServertoRandomAccessibleInterval<T>where we know thatTis an instance ofRealTypebut we don't know what exactly it is.