-
Notifications
You must be signed in to change notification settings - Fork 20
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
Add Hessian eigenvalues #29
Conversation
- Unified RealCompositeMatrix classes - eigenvalues for non-symmetric matrices possible now
That's what the interface requires: http://commons.apache.org/proper/commons-math/apidocs/org/apache/commons/math3/linear/RealMatrix.html#copy()
…uesTest e7b31ff added separate test for Hessian matrix making the existing consistency test obsolete.
Use [email protected] as contact. Change if requested by @tpietzsch.
@tinevez's commit tinevez/CircleSkinner@84e4569 reminded me of this outstanding pull request. I should have some time next week to clean-up and simplify the interface. Specifically, I will change the interface such that all inputs must be supplied by the caller. That might be as extreme as having an interface that accepts only the gradient image. That way the number of overloads would be greatly reduced. Also, I would like to get rid of one of func( args..., int nThreads, ExecutorService es) and func( args..., int nThreads ) Currently, I lean towards keeping the first option and removing the second one. Another option would be to get rid of the parallel computing in its entirety as this can be done by the caller by calculating the Hessian on Chunks of data. That would greatly reduce the number of overloads. Thinking about the ImageJ picture, this should probably go into ops @ctrueden @dietzc . If you agree, I will write an op once this is merged in imglib2-algorithm. I will approach you with questions once I get there. |
Hi @hanslovsky, I'd keep the variant with |
Hi @hanslovsky , Here is a question regarding the Hessian calculation. But for the Hessian to be calculated correctly, the partial derivatives should be scaled accordingly too right? |
@dietzc What I don't like about the // FIXME find better heuristic?
final int numThreads = Runtime.getRuntime().availableProcessors();
final int numTasks = numThreads <= 1 ? 1 : numThreads * 20; I will keep the @tinevez I will have to think about that! |
@tinevez I think the appropriate way to get partial derivatives right is to re-sample the (smoothed) input image for isotropy. I would leave it to the caller to do that. What I add instead is a View that takes a Hessian matrix as input and scales the second derivatives according to a sigma parameter (the same you would use for smoothing), something like this: public static < T extends RealType< T > > AbstractConvertedRandomAccessible< T, T > scaleHessian( final RandomAccessible< T > hessian, final T t, final double... sigma )
{
final double minSigma = Arrays.stream( sigma ).reduce( Double.MAX_VALUE, ( d1, d2 ) -> Math.min( d1, d2 ) );
final double minSigmaSq = minSigma * minSigma;
final double[] scales = new double[ sigma.length * ( sigma.length + 1 ) / 2 ];
for ( int i1 = 0, k = 0; i1 < sigma.length; ++i1 )
for ( int i2 = i1; i2 < sigma.length; ++i2, ++k )
scales[ k ] = sigma[ i1 ] * sigma[ i2 ] / minSigmaSq;
final AbstractConvertedRandomAccessible< T, T > wrapped = new AbstractConvertedRandomAccessible< T, T >( hessian )
{
@Override
public AbstractConvertedRandomAccess< T, T > randomAccess()
{
return new AbstractConvertedRandomAccess< T, T >( hessian.randomAccess() )
{
private final T tt = t;
@Override
public T get()
{
tt.set( source.get() );
tt.mul( scales[ source.getIntPosition( sigma.length ) ] );
return tt;
}
@Override
public AbstractConvertedRandomAccess< T, T > copy()
{
// TODO Auto-generated method stub
return null;
}
};
}
@Override
public AbstractConvertedRandomAccess< T, T > randomAccess( final Interval interval )
{
return randomAccess();
}
};
return wrapped;
} Note that the scaling factors are normalized by the smallest sigma squared. The scaling has to be quadratic because the hessian matrix contains second derivatives. This should have minimal overhead and you could even write the contents of the view back into the hessianMatrix in a single pass without allocation of extra memory. Let me know what you think. |
Non-symmetric matrices may have complex eigenvalues. This interface change is thus required for compatibility with use cases other than Hessian matrix (symmetric) eigenvalues.
…ompute Now, matrix based EigenValue implementations hold a RealCompositeMatrix object with exchangable data Composite instead of creating an instance at each call of compute. As a consequence EigenValues are not thread safe in general and a copy method was added to the interface.
Replace the checks by asserts and if the caller has input that might be out of bounds the check should be done outside getEntry.
…rom RealComposite to Composite. We do not care about the kind of composite that is passed. We only need to call Composite.get.
I added a convenience method to scale the hessian matrix based on a sigma[] parameter passed by the caller (cf the last commit @tinevez ). I also added the missing license headers (how did I miss them?) Is this ready to be merged now? |
I could use this PR, once merged, in imagej-ops to port the tubenessop. |
ojAlgo v43 was just released so we can migrate to ojAlgo.
ojAlgo v43 was just released: optimatika/ojAlgo@5487fa8 Update: Once this is confirmed for a merge I will make a rebase into a single commit. |
Hessian eigenvalues ojalgo
Eclipse (neon.2) seems to have issues indenting parameters following generic wildcards.
@/all Is this ready for a merge? |
It is from my side (after a rebase, of course). |
I'm looking at it now |
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 left a few inline suggestions/comments, but from my side this is in principle ready for merging.
Cool that you are using ojAlgo.
In general, I would be interested in the trade-offs, with respect to performance and expressiveness, of Vector/Matrix NativeTypes
vs. the Composite
approach. Unfortunately I have no time to look into it now...
final int nDim = source.numDimensions(); | ||
if ( nDim < 2 ) | ||
{ | ||
gradientCentralDifference2( source, gradient, 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.
Why are you calling gradientCentralDifference2
, not gradientCentralDifference
in this case?
Is it faster? Did you test?
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.
No particular reason: I will change that to gradientCentralDifference
to be consistent with what happens in HessianMatrix
and use the faster version (according to the comments in PartialDerivative
.
assert sigma.length * ( sigma.length + 1 ) / 2 == hessian.dimension( sigma.length ); | ||
final int maxD = sigma.length; | ||
|
||
final double minSigma = Arrays.stream( sigma ).reduce( Double.MAX_VALUE, ( d1, d2 ) -> Math.min( d1, d2 ) ); |
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.
final double minSigma = Util.min( sigma )
would be more readable. (net.imglib2.util.Util
)
return hessianMatrix; | ||
} | ||
|
||
public static < T extends RealType< T > > IntervalView< T > scaleHessianMatrix( final RandomAccessibleInterval< T > hessian, final double[] sigma ) |
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.
Can you add javadoc? It's not obvious what this method does
this( data, nRows, nCols, nRows * nCols ); | ||
} | ||
|
||
public RealCompositeMatrix( final Composite< T > data, final int nRows, final int nCols, final int length ) |
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 make this constructor (and all constructors of derived classes having a length
argument) protected. The constructor only makes sense for being able to derive classes. In all other scenarios the length
element is redundant.
expectedLength()
can be removed then.
* @author Philipp Hanslovsky | ||
* | ||
*/ | ||
public class HessianMatrix |
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 wonder whether it is worth adding a version that takes gaussian
and gradient
arguments that have appropriate size so that an OutOfBounds
factory is not required. This would be useful if you want to do the Hessian computation blockwise to avoid artifacts at block boundaries. Of course, this can also be added later when it is needed.
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 have not thought about that but I like the idea. I did not like the extra OufOfBounds
so we should keep that in our minds. I have only one concern which actually might be a non-issue: If you already have the gradients pre-computed, you can just call the overload takes only the gradients as input. If you do not have them pre-computed, you will probably want to re-use them as features elsewhere and you might want to have them at the same size as the input.
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, I fully agree
* n+1-dimensional {@link RandomAccessibleInterval} for storing | ||
* the gradients along all axes of the smoothed source (size of | ||
* last dimension is n) | ||
* @param hessianMatrix |
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.
Can you add a javadoc description of the upper triangular layout of the hessianMatrix (can be adapted from TensorEigenValues javadoc)?
{ | ||
// TODO Auto-generated catch block | ||
e.printStackTrace(); | ||
} |
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.
e.printStackTrace()
is probably not what should happen here.
This is a problem also in other algorithms (e.g., DifferenceOfGaussian
, SeparableSymmetricConvolution
, ...).
So I would merge this anyway and open an issue to fix it in all places.
For InterruptedException
the right thing to do would be to restore the interruption status of the current thread and return.
For ExecutionException
I don't know ...
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.
Quoting @tpietzsch "The constructor only makes sense for being able to derive classes. In all other scenarios the length element is redundant. expectedLength() can be removed then."
@tpietzsch rightfully mentioned that it was not clear what scaleHessianMatrix does. Added some JavaDoc for clarification.
#41 is the same pull request but from a rebased branch to have a single commit. |
This pull request features:
For this purpose, these classes and methods were added:
net.imglib2.algorithm.corner
:RealComposite*Matrix
: Implementation of apacheRealMatrix
as a View on imglibRealComposite
to avoid unnecessary allocation and copyHessianMatrix
: Static methods to compute Hessian matrix from n-dimensional input.TensorEigenValues
: Static methods to compute tensor from n+1-dimensional input, where the last dimension stores linear representation of the tensor.net.imglib2.algorithm.gradient
:PartialDerivative
: Added static methodgradientCentralDifferenceParallel
to speed up gradient computation.Before merging this, please comment on the package structure:
RealComposite*Matrix
get their own package?TensorEigenValues
be in a package of its own, e.g.net.imglib2.algorithm.tensor
?Once the pull request is ready to be merged, I will rebase in my local repository and create a new pull request.