[ImageJ-devel] [fiji-devel] Significant change to ImgOpener - or - why ImgOpener.openImg() cannot return a T

Stephan Preibisch preibisch at mpi-cbg.de
Tue Jul 31 17:18:02 CDT 2012


Hi Barry, hi Curtis,

the example you give would give you a warning, and, yes, - a runtime error. The problem is, there is no right way to do it as the type is not defined at compile time but instead depends on the image that is opened. 

However, I do not see why <? extends RealType> would have any advantage. It crashes on the exactly same thing, but is more tough to write, see here:

Img< RealType >img = ImgOpener.open( "/Users/preibischs/Documents/Microscopy/SPIM/HisYFP-SPIM/spim_TL18_Angle0.lsm" );
img.firstElement().add( new FloatType() );

-> fails because cannot cast to ByteType (what the image is)

Img< ? extends RealType >img = ImgOpener.open( "/Users/preibischs/Documents/Microscopy/SPIM/HisYFP-SPIM/spim_TL18_Angle0.lsm" );
img.firstElement().add( new FloatType() );

-> fails as well, but is more to write

And the construct <? extends RealType<?>> would be correct but cannot be used in any generic method as it is not ensured that the first and the second "?" are the same. 

We still have to choose one of them, I still vote for Img< RealType >, because it is at least easy to write.

The best is to open it as FloatType or as ByteType, but this is not always possible ...

What do you think? 

Bye bye,
Steffi


On Jul 31, 2012, at 17:59 , Curtis Rueden wrote:

> Hi Barry,
> 
> 
> This is just an example I've pulled from the web but it seems to imply that an Img<UnsignedByteType> is not safely considered an Img<RealType>.
> 
> Good point! We should probably use "Img<? extends RealType>" instead. I tested this with ImageJ2, and it still works as expected. And I believe all of Steffi's example cases will still work too. So I pushed these changes to the newimgopener branch.
> 
> What do you think, Steffi? Anyone else?
> 
> Regards,
> Curtis
> 
> 
> On Tue, Jul 31, 2012 at 4:28 PM, Barry DeZonia <bdezonia at gmail.com> wrote:
> Steffi,
> 
> I remember running into this when originally interfacing IJ2 to Imglib. We did decided to go with Img<? extends RealType<?> as that seemed the most safe at the time.
> 
> What I'm wondering about with your solution is that it seems to contradict other people's description as to what is legal to do in Java.
> 
> As a contrived example think of the animal cage example:
> 
> We have a Animal class and a Lion class derived from it. And a Cage class that is defined Cage<T extends Animal>.
> 
> Though Lion is instanceof Animal its not the case that Cage<Lion> instanceof Cage<Animal>. This is counterintuitive but many people mention it.
> 
> If it was the case you could write code like this:
> 
> 
> Cage<Animal> animals;
> 
> 
> 
> Cage<Lion> lions;
> 
> 
> 
> animals = lions; // This assignment is not allowed
> 
> 
> 
> animals.add(rat); // If this executed, we'd have a Rat in a Cage<Lion>
> 
> This is just an example I've pulled from the web but it seems to imply that an Img<UnsignedByteType> is not safely considered an Img<RealType>. We may be avoiding all such cases where it could be a problem but is it safe going forward? I can't say I know.
> 
> 
> On Tue, Jul 17, 2012 at 4:08 PM, Stephan Preibisch <preibisch at mpi-cbg.de> wrote:
> Hi guys,
> 
> this became a massive explanation. I suggest this as as solution to a problem we have been only partly aware of. I hope you enjoy reading ;)
> 
> Steffi
> 
> 
> As the ImageJ conference is approaching I was talking with Johannes and we agreed that the ImgOpener needs to be finished. However, since its first version there has been a major design fault.
> 
> But first the good news:
> --------------------------------
> It works perfectly fine if you say "open me an Image as float" or "...as UnsignedByte" or whatever, classically called by the method
> -> new ImgOpener.openImg( String id, ImgFactory<T> factory, T type );
> It can, without any problems, return you an Img<T>, and it requires that T is RealType (and not anymore NativeType, see at the end). So far, so good.
> Note that "T" is NOT a return parameter, but something you give to the openAs method.
> 
> Now the bad news.
> --------------------------
> If you say "open this image as whatever RealType it is", it can do that, but it cannot assign a "T" to it - because T is not a return parameter. We made an ugly hack inside so that it seems as if it would work, but it does not. Now you might ask, why change it if it worked so far? Well, here is an easy example that would cause a ClassCastException on run time
> 
> public static <T extends RealType< T >> void main( String[] args )
> {
>         Img< T > img1 = new ImgOpener.openImg( "somepic_8bit.tif" ); // 8-bit unsigned
>         Img< T > img2 = new ImgOpener.openImg( "somepic_32bit.tif" ); // 32-bit float
> 
>         img1.firstElement().set( img2.firstElement() ); // run-time crash
> }
> 
> It will throw a ClassCastException because img1 is of UnsignedByteType and img2 of FloatType. But as we cast it in a dirty way, it compiles just fine.
> So, we cannot return a "T", but what we can return is Img< which is at least a RealType >. And this is for sure always true, but img1 and img2 are not necessarily the same RealType (as above).
> 
> The correct way (which doesn't work)
> --------------------------------------------------
> What we should return is an Img< ? extends RealType< ? > >. However, it is not ensured that the two "?" are the same, so an Img of this type is basically incompatible with everything else, which means an unchecked cast is necessary. So although correct, maybe not a good idea. And it is really ugly to write if necessary.
> 
> The still somehow correct way
> -----------------------------------------
> Instead, it returns now an Img< RealType >. This is kind of a tradeoff. On one hand, this is very easy to write and will give you compile errors where it should, for example
> 
>         img1.firstElement().set( img2.firstElement() ); // COMPILE ERROR (not the same RealType realization)
> 
> OR
> 
>        public <T extends RealType< T >> void add( IterableInterval< T > i1, IterableInterval< T > i2 ) { .... }
>        add( img1, img2 ); // COMPILE ERROR (not the same RealType realization)
> 
> BUT
> 
>         Gauss.inFloatInPlace( 2.0 , img1 ); // FINE (just one RealType realization required, inside it will be always the same "T")
>         Gauss.inFloatInPlace( 2.0 , img2 ); // FINE (just one RealType realization required, inside it will be always the same "T")
> 
>         public < T extends RealType< T > > void add1( Img< T > img1, double value ) { ... }
>         add1( img1, 5 ); // FINE (just one RealType realization required, inside it will be always the same "T")
> 
>         public < T extends RealType< T >, S extends RealType< S > > void add2( Img< T > img1, Img< S > img2 ) { ... }
>         add2( img1, img2 ); // FINE (explicitly two different RealType realizations are allowed)
> 
>         public void add3( Img< RealType > img1, Img< RealType > img2 ) { ... }
>         add3( img1, img2 ); // FINE (both are just some kind of RealType, but gives a warning)
> 
> On the other hand it gives a lot of Warnings because RealType should be more specified.
> 
> Why not Img< RealType< ? > >
> ------------------------------------------
> Similar problem as in <? extends RealType< ? > >. RealType< ? > is not a valid substitute for any construct like < T extends RealType < T > >. One would have to cast to Img< RealType >, so we can take this one right away as it is less writing.
> 
> Where did NativeType go?
> ------------------------------------
> I do not see any reason why we should enforce a NativeType. There is no objection to load an image into a (hypothetical) ListImg< BigDecimalType >.
> 
> What are the implications?
> -----------------------------------
> We should write NOTHING except the method opening files for Img< RealType >. And also only if it is really required - quite often it is not. But if, Img< RealType > It is a completely valid input for any generic algorithm as show above for Gauss, add, etc.
> 
> 
> 
> I implemented the changes on a branch called 'newimgopener'. It also contains four static convenience opening methods and a speed improvement.
> 
> Any comments are very welcome. If somebody has a better idea how to solve the problem ... I am all ears ...
> 
> Bye bye,
> Steffi
> 
> --
> Please avoid top-posting, and please make sure to reply-to-all!
> 
> Mailing list web interface: http://groups.google.com/group/fiji-devel
> 
> 
> -- 
> Please avoid top-posting, and please make sure to reply-to-all!
>  
> Mailing list web interface: http://groups.google.com/group/fiji-devel
> 
> 
> -- 
> Please avoid top-posting, and please make sure to reply-to-all!
>  
> Mailing list web interface: http://groups.google.com/group/fiji-devel

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://imagej.net/pipermail/imagej-devel/attachments/20120731/f89db42c/attachment-0001.html>


More information about the ImageJ-devel mailing list