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

Curtis Rueden ctrueden at wisc.edu
Tue Jul 31 16:59:26 CDT 2012


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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://imagej.net/pipermail/imagej-devel/attachments/20120731/aff93144/attachment.html>


More information about the ImageJ-devel mailing list