-
Notifications
You must be signed in to change notification settings - Fork 19
Add single-threaded version to LocalExtrema and allow for caller specified neighborhoods. #37
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
…ified neighborhoods. This commit adds: - shape parameter for callers to specify local neighborhood - Single-threaded version of findLocalExtrema - Parameter for caller to specify number of tasks. - Note in the JavaDoc to inform user that border pixels are ignored. - Relax the bounds on generic parameter T: Was Comparable< T >, now unbounded. The original interface is preserved for a non-breaking change. Code coverage is now at 88% for LocalExtrema.java, only missing: - Exception handlings for parallel execution - assert statements - MinimumCheck class See also: https://gitter.im/imglib/imglib2?at=58bd72101465c46a56f2ae9c Add multi-threaded interface to accept RandomAccessible and Interval This way the implementation is much cleaner. The RandomAccessibleInterval interface remains unchanged. Fix bug in determining the maximum size of source Add license and author tag to LocalExtremaTest Add more comprehensive test for better code coverage. The previously existing interface was not tested. It is tested now. Code coverage is now at 88% for LocalExtrema.java, only missing: - Exception handlings for parallel execution - assert statements - MinimumCheck class
| // TODO It is probably better to throw exception than to use try/catch | ||
| // block and return potentially incomplete/inconsistent list of extrema. | ||
| // Returning an empty list on exception could be a compromise without | ||
| // changing the interface. |
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 agree, throwing an exception is the better way!
Let's change the API, and add a deprecated version with the old signature that prints the exception and returns null.
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.
Sounds reasonable to me! I will add that.
As we are at interface changes already, what about @tinevez suggestion in #33 :
#33 (review)
We could return a List in the new interface but cast to ArrayList before returning in the deprecated version because we know the implementation details.
I still think that we should stick with ArrayList because that is unlikely to change but I'd like to hear your opinion on it.
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.
In the original implementation the task was split along the last dimension. Should I revert to that for the deprecated version for consistency?
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.
Yes, sounds good. I'm for changing it to List. (or even Collection, but that's probably overdoing it...)
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.
And yes, I would revert to splitting along the last dimension
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 will change it to List, then. And revert to spliiting along the last dimension for the deprecated version.
| { | ||
| final RandomAccessible< Neighborhood< Object > > neighborhood = shape.neighborhoodsRandomAccessible( ConstantUtils.constantRandomAccessible( new Object(), nDim ) ); | ||
| final long[] min = LongStream.generate( () -> Long.MAX_VALUE ).limit( nDim ).toArray(); | ||
| final long[] max = LongStream.generate( () -> Long.MIN_VALUE ).limit( nDim ).toArray(); |
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 probably sound like a grumpy old man here, but what's wrong with Arrays.fill(max, Long.MIN_VALUE)?
:-)
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 am using streams because Java still lacks a function that generates an array that's filled with a constant value in a single line, something like Arrays.create( int length, long value ) would be great. This is my little workaround for that. ;) At least for me, readability is improved wthat way.
| max[ d ] = Math.max( bb.max( d ), max[ d ] ); | ||
| } | ||
|
|
||
| final long[] borderSize = IntStream.range( 0, nDim ).mapToLong( d -> Math.max( max[ d ], -min[ d ] ) ).toArray(); |
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.
Similar here. Why not simply put borderSize[ d ] = Math.max( max[ d ], -min[ d ] ); into the above for loop? To me it looks more readable, but that is of course a personal preference...
(Also it's probably much faster, but that isn't important here)
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.
Again, I prefer the one-liner but that might be because I am coming from the python/numpy world originally where I would do something like numpy.maximum( max, -min )
| final long[] borderSize = getRequiredBorderSize( shape, source.numDimensions() ); | ||
| final int nDim = source.numDimensions(); | ||
| // Get biggest dimension after border subtraction. Parallelize along | ||
| // this dimension. |
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'm not sure this is always a good choice. Assuming some kind of flattened array, where the highest dimension is the slowest changing, it is probably preferable to choose that one because of cache utilisation (even if the extent is larger along another dimension). It would be interesting to see a benchmark.
In the end, of course it can be a rotated view or the highest dimension may have extent 1, etc...
Can you add a version where the parallelisation dimension is a parameter?
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.
Good point, I will add that and this method should delegate to it!
- Add @deprecated to deprecated method - Revert to parallelizing over last dimension for deprecated method - Add method signatures with split dimension as parameter - Return `List` instead of `ArrayList` (deprecated method still returns `ArrayList`) The test cases have been made more comprehensive and miss only assertion statements and exception handling in the deprecated method.
|
Can this be merged now? |
|
|
||
| assert Arrays.stream( borderSize ).min().getAsLong() >= 0: "Border size cannot be smaller than zero."; | ||
|
|
||
| // TODO use Intervals.expand once it is available in non-SNAPSHOT |
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 just released imglib2-4.6.0. The Intervals.expand should be now available.
|
I just released imglib2-4.6.0. The Then it's ready to go! (There is a javadoc link error in the class comment. It was there already before, but maybe while you're at it, could you fix it?) |
|
Great, I'll take care of the Update:
|
|
I think that overriding the one dependency version that you care for is ok? |
|
If that is the general policy, I can of course go ahead and add the required changes. Is that ok only while on a SNAPSHOT version, i.e. overriding the dependency version should not happen in a release version? |
|
Just to describe the general SciJava policy: for projects in There is a script to facilitate meeting this requirement here. Personally I would prefer and recommend that
If I understand @axtimwalde's suggestion correctly, you can just override
That seems like a reasonable rule of thumb. That is: when a project is about to be released, we make an effort to eliminate the pinned version properties. However, I think there are cases (e.g., a bunch of components in a dependency chain, where work was done at the lowest levels that needs to propagate downstream) where it can be burdensome. So I'd hesitate to make this a hard requirement. |
|
Thanks for the clarification @ctrueden |
It is not a concern here. Rather, the problems typically happen downstream somewhere. For example, suppose I have a project which extends the latest In my experience, the easiest way to avoid these kinds of problems is to keep This is, BTW, one big reason why I think maintaining a separate |
|
I saw that |
|
This happened in #50 I merged master into this branch so this should be ready to be merged now without version skew. |
|
@imglib/admins can we merge this? From the discussion it seems that the imglib2 dependency version was the only remaining issue, which seems to be resolved now. I do not want to merge my own PR (as a non-owner), though. |
|
Pleas merge. |
This commit adds:
The original interface is preserved for a non-breaking change.
Code coverage is now at 88% for LocalExtrema.java, only missing:
See also:
https://gitter.im/imglib/imglib2?at=58bd72101465c46a56f2ae9c
This PR is a rebase of and closes #33