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
_______________________________________________
ImageJ-devel mailing list
[email protected]
http://imagej.net/mailman/listinfo/imagej-devel

Reply via email to