-
Notifications
You must be signed in to change notification settings - Fork 95
Add utility class to wrap images as 1D collections #248
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
A couple of notes.
|
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 like the idea of this commit. Enabling the conversions:
- InterableInterval -> Collection
- RandomAccessibleInterval -> List
is nice. But I don't like that we need to add so many methods. We would need to add even more methods for each new type, and collection class supported. I would definitely drop the support for boxed Byte and Short.
One could avoid having so many methods, be using some builder pattern. Or providing a type converter as parameter just like Converters.convert(image, converter, type).
Another thougth: This doesn't seem to be so much imglib2-core
to me. The functionality is comparable to ImageJFunctions.wrap(...) in imglib2-ij. I personally would consider adding it to algorithms.
public final class FlatCollections | ||
{ | ||
|
||
public static < T, E > Collection< E > collection( final IterableInterval< T > ii, final Function< T, E > converter ) |
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 it would be nice to use
image
as a parameter name rather thanii
. - The method would benefit from a javadoc.
return collection( ii, t -> t.getBigInteger() ); | ||
} | ||
|
||
public static < T, E > List< E > list( final RandomAccessibleInterval< T > ii, final Function< T, E > converter ) |
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.
- First parameter must not be named
ii
. Useimage
instead. - Javadoc would be nice
} | ||
} | ||
|
||
private static int size32( final long size ) |
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.
- Name
size32
doesn't make much sense.sizeAsInt
would be better.
return collection( ii, t -> t.get() ); | ||
} | ||
|
||
public static Collection< Byte > byteCollection( final IterableInterval< ByteType > ii ) |
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.
This method is not necessary. It's performance and memory consumption is probably the same as integerCollection. And ByteType
is also not very widely used.
return collection( ii, t -> t.getIntegerLong() ); | ||
} | ||
|
||
public static Collection< Short > shortCollection( final IterableInterval< ShortType > ii ) |
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.
Not needed integerCollection
could be used instead
return list( ii, t -> t.get() ); | ||
} | ||
|
||
public static List< Byte > byteList( final RandomAccessibleInterval< ByteType > ii ) |
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.
Remove method. integerList
can be used instead.
return list( ii, t -> t.getIntegerLong() ); | ||
} | ||
|
||
public static List< Short > shortList( final RandomAccessibleInterval< ShortType > ii ) |
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.
Remove method. integerList
can be used instead.
|
||
private final Img< ShortType > imgShort = ArrayImgs.shorts( new short[] { 32767, 32766, 32765, 32764, 32763, 10000, 10001, 10002, 10003, -32768, -32767, -32766 }, 2, 3, 2 ); | ||
|
||
private final Img< UnsignedLongType > imgBig = ArrayImgs.unsignedLongs( new long[] { 2 * Long.MAX_VALUE, 3 * Long.MAX_VALUE, 4 * Long.MAX_VALUE, Long.MIN_VALUE, Long.MIN_VALUE / 2, Long.MIN_VALUE / 3 }, 3, 2 ); |
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 would not test that many values. Even testing one value per type should be enough.
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.
There is no performance issue with these tests. Testing more edge cases is good. I see no benefit in cutting down these tests.
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 also see no performance issue. My points are these:
- The tests could be simpler. A simple test, makes maintenance easier. When it fails, finding the bug is easier.
- The test implement the same algorithm as the tested code. If there's a bug with for example the usage of IntervalIndexer, the test want show that, because it would compare the wrong result against an equally wrong expected result. So having a test, which is simple enough to not use for example IntervalIndexer is actually a benefit.
- There are better places to put test
getRealDouble
and similar methods. See PR Simple Conversion between RealTypes #249 fixes a bug in Unsigned128BitInteger.getRealDouble and also adds tests.
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.
On the other side. The tests can stay as the are they are good enough.
@Test | ||
public void testCollection() | ||
{ | ||
assertImageEqualsCollection( imgString, FlatCollections.collection( imgString, s -> s ), Objects::equals ); |
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.
Could be instead:
List<String> pixels = Array.asList("A", "B","C","D");
Img<String> img = new ListImg(pixels, 2, 2);
List<String> result = FlatCollections.collection(img, s -> s);
assertCollectionsEquals(pixels, result);
@Test | ||
public void testIntegerCollection() | ||
{ | ||
assertImageEqualsCollection( imgByte, FlatCollections.integerCollection( imgByte ), ( e, a ) -> e.getInteger() == a ); |
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.
Could be simpler, if we test only for one value:
assertCollectionEquals( Arrays.asList(42), FloatCollections.integerCollection(ArrayImgs.bytes(byte[]{42},1)) );
This is useful to pass images to APIs that work with collections, such as Guava's com.google.common.math.Quantiles. See also: https://forum.image.sc/t/imglib2-development-a-question-about-img-t/23186/8
6c9196c
to
2f58e5a
Compare
Thanks for the review, @maarzt! I force-pushed an update with the following improvements:
I did not (yet) change either of the following:
I think it is a mistake to cut down the testing. Fewer edge cases would be covered. The tests as written are simple to understand and not burdensome to maintain. Regarding removal of the byte and short signatures: generally speaking, I agree with you that less code is better. Every line of code is a liability, as they say. However, in this case, there are 8 primitive types, and each of them (except Finally, I disagree that this utility class would be better served in imglib2-algorithm, for the following reasons:
If it is decided that this utility class is too "bloaty" for ImgLib2 core, I'd favor just putting it into ImageJ Ops instead. But I think it would be a shame to reduce its visibility that way. |
After thinking about it a little bit more. I agree that this PR is mostly fine as it is. Tests are ok, having all the methods is ok, having it as part of imglib2 is fine for me too. But I still have some worries:
|
They only work for ByteType and ShortType respectively, which are not very frequently used. And it is rare in practice to need specifically a Collection or List of these primitive types. They can still be done by calling the general methods with a custom function.
For Collection, the order will always match the IterableInterval. For List, the order will always be flat.
I understand what you are saying about the tests. The way they are written, they are regression tests. They aim to be a little more thorough (exercising multiple methods of I pushed two more commits: one removing the byte and short wrappers, and one adding a note to the javadoc about the iteration order. Regarding performance of the wrapped collections, there should be virtually no memory overhead. Yes, there is boxing. However, it's not like |
The typing can be relaxed a bit. Now it's possible to use methods in generic code, without a type variable: Img< ? extends RealType< ? > > image = ...; List< Double > list = FlatCollections.doubleList( image ); Previously the method call required a type variable <T extends RealType<T>>: Img< T > image = ...; List< Double > list = FlatCollections.doubleList( image );
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.
Looks ready to merge
Don't use IntervalIndexer it makes the test unnecessary complicated. Call different methods to test the list. Avoid number overflow when specifying values for image of UnsignedLongType.
This provides utility classes converting from II<RealType> and RAI<RealType> to Collection<Double> and List<Double>, respectively.
Thanks @maarzt! |
This is useful to pass images to APIs that work with collections, such as Guava's
com.google.common.math.Quantiles
.See also this post on the Image.sc Forum.