Skip to content

Commit

Permalink
Merge pull request #13 from mastodon-sc/compute-ellipsoid-for-segment…
Browse files Browse the repository at this point in the history
…ation-2

Add an example that demonstrates how to compute the ellipsoid parameters for a given segmentation result.
  • Loading branch information
maarzt authored Dec 6, 2023
2 parents 4b10c5b + 64609f2 commit 0f054d0
Show file tree
Hide file tree
Showing 8 changed files with 201 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ public void installGlobalActions( final Actions actions )
actions.namedAction( fitSelectedVerticesAction, FIT_SELECTED_VERTICES_KEYS );
}

void fitSelectedVertices()
public void fitSelectedVertices()
{
// TODO: parameters to select which source to act on
final int sourceIndex = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class MinimalProjectModel

private final SelectionModel< Spot, Link > selectionModel;

MinimalProjectModel(
public MinimalProjectModel(
final Model model, final SharedBigDataViewerData sharedBdvData, final SelectionModel< Spot, Link > selectionModel
)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@

import org.junit.Test;
import org.mastodon.collection.RefObjectMap;
import org.mastodon.mamut.fitting.FitEllipsoidPlugin;
import org.mastodon.mamut.fitting.ellipsoid.Ellipsoid;
import org.mastodon.mamut.fitting.util.ArtificialData;
import org.mastodon.mamut.model.ModelGraph;
import org.mastodon.mamut.model.Spot;
import org.scijava.Context;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package org.mastodon.mamut.fitting.demo;

import net.imglib2.Cursor;
import net.imglib2.img.Img;
import net.imglib2.img.array.ArrayImgs;
import net.imglib2.loops.LoopBuilder;
import net.imglib2.type.numeric.real.FloatType;
import net.imglib2.util.LinAlgHelpers;

import org.mastodon.mamut.fitting.util.DemoUtils;
import org.mastodon.mamut.fitting.util.MultiVariantNormalDistributionRenderer;
import org.mastodon.mamut.model.Model;

import java.util.Arrays;

/**
* Computing the mean position and covariance matrix for a given segmented
* region of an image is an easy way to get good ellipsoid parameters for
* that segment.
* <p>
* Here is an example of how to do that.
*/
public class ComputeMeanAndVarianceDemo
{

public static void main( String[] args )
{
double[] center = { 40, 50, 60 };
double[][] givenCovariance = {
{ 400, 20, -10 },
{ 20, 200, 30 },
{ -10, 30, 100 }
};

long[] dimensions = { 100, 100, 100 };
int background = 0;
int pixelValue = 1;
Img< FloatType > image = generateExampleImage( center, givenCovariance, dimensions, background, pixelValue );
double[] mean = computeMean( image, pixelValue );
double[][] computedCovariance = computeCovariance( image, mean, pixelValue );

System.out.println( "Given center: " + Arrays.toString( center ) );
System.out.println( "Computed mean: " + Arrays.toString( mean ) );
System.out.println( "Given covariance: " + Arrays.deepToString( givenCovariance ) );
System.out.println( "Computed covariance: " + Arrays.deepToString( computedCovariance ) );

Model model = new Model();
model.getGraph().addVertex().init( 0, mean, computedCovariance );
DemoUtils.showBdvWindow( DemoUtils.wrapAsAppModel( image, model ) );
}

/**
* Returns an example image with a single ellipsoid.
*
* @param center center of the ellipsoid
* @param cov covariance matrix of the ellipsoid
* @param dimensions dimensions of the image
* @param background value of the background
* @param pixelValue value of the ellipsoid
*/
private static Img< FloatType > generateExampleImage(
final double[] center, final double[][] cov, final long[] dimensions, final int background, final int pixelValue
)
{
Img< FloatType > image = ArrayImgs.floats( dimensions );
MultiVariantNormalDistributionRenderer.renderMultivariateNormalDistribution( center, cov, image );
LoopBuilder.setImages( image ).forEachPixel( pixel -> {
if ( pixel.get() > 500 )
pixel.set( pixelValue );
else
pixel.set( background );
} );
return image;
}

/**
* Computes the mean position of the pixels whose value equals the given {@code pixelValue}.
*
* @param image the image
* @param pixelValue the pixel value
* @return the mean position
*/
private static double[] computeMean( final Img< FloatType > image, final int pixelValue )
{
Cursor< FloatType > cursor = image.cursor();
double[] sum = new double[ 3 ];
double[] position = new double[ 3 ];
long counter = 0;
while ( cursor.hasNext() )
if ( cursor.next().get() == pixelValue )
{
cursor.localize( position );
LinAlgHelpers.add( sum, position, sum );
counter++;
}
LinAlgHelpers.scale( sum, 1. / counter, sum );
return sum;
}

/**
* Computes the covariance matrix of the pixels whose value equals the given {@code pixelValue}.
*
* @param image the image
* @param mean the mean position
* @param pixelValue the pixel value
*/
private static double[][] computeCovariance( final Img< FloatType > image, final double[] mean, final int pixelValue )
{
Cursor< FloatType > cursor = image.cursor();
long counter = 0;
double[] position = new double[ 3 ];
double[][] covariance = new double[ 3 ][ 3 ];
cursor.reset();
while ( cursor.hasNext() )
if ( cursor.next().get() == pixelValue )
{
cursor.localize( position );
LinAlgHelpers.subtract( position, mean, position );
for ( int i = 0; i < 3; i++ )
for ( int j = 0; j < 3; j++ )
covariance[ i ][ j ] += position[ i ] * position[ j ];
counter++;
}
scale( covariance, 5. / counter ); // I don't know why the factor 5 is needed. But it works.
return covariance;
}

private static void scale( final double[][] covariance, final double factor )
{
for ( int i = 0; i < 3; i++ )
for ( int j = 0; j < 3; j++ )
covariance[ i ][ j ] *= factor;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,13 @@
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
package org.mastodon.mamut.fitting;
package org.mastodon.mamut.fitting.demo;

import javax.annotation.Nonnull;

import org.mastodon.mamut.ProjectModel;
import org.mastodon.mamut.views.bdv.MamutViewBdv;
import org.mastodon.mamut.fitting.FitEllipsoidPlugin;
import org.mastodon.mamut.fitting.util.ArtificialData;
import org.mastodon.mamut.fitting.util.DemoUtils;
import org.scijava.Context;

import net.imglib2.type.numeric.RealType;

/**
* Runs the {@link FitEllipsoidPlugin} on synthetic data
Expand All @@ -49,11 +47,6 @@ public static void main( final String... args )
final FitEllipsoidPlugin plugin = new FitEllipsoidPlugin();
plugin.setAppPluginModel( data.getAppModel() );
plugin.fitSelectedVertices();
showBdvWindow( data.getAppModel() );
}

private static < T extends RealType< T > > void showBdvWindow( @Nonnull final ProjectModel appModel )
{
appModel.getWindowManager().createView( MamutViewBdv.class );
DemoUtils.showBdvWindow( data.getAppModel() );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,31 +26,22 @@
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
package org.mastodon.mamut.fitting;
package org.mastodon.mamut.fitting.util;

import java.util.Objects;
import java.util.Random;

import org.mastodon.collection.RefObjectMap;
import org.mastodon.collection.ref.RefObjectHashMap;
import org.mastodon.mamut.ProjectModel;
import org.mastodon.mamut.fitting.MinimalProjectModel;
import org.mastodon.mamut.fitting.ellipsoid.Ellipsoid;
import org.mastodon.mamut.model.Link;
import org.mastodon.mamut.model.Model;
import org.mastodon.mamut.model.Spot;
import org.mastodon.model.DefaultSelectionModel;
import org.mastodon.model.SelectionModel;
import org.mastodon.views.bdv.SharedBigDataViewerData;
import org.scijava.Context;

import ij.ImagePlus;
import net.imagej.ImgPlus;
import net.imagej.axis.Axes;
import net.imagej.axis.AxisType;
import net.imglib2.Interval;
import net.imglib2.img.Img;
import net.imglib2.img.array.ArrayImgs;
import net.imglib2.img.display.imagej.ImgToVirtualStack;
import net.imglib2.realtransform.AffineTransform3D;
import net.imglib2.type.numeric.real.FloatType;
import net.imglib2.util.Intervals;
Expand Down Expand Up @@ -97,9 +88,7 @@ public ArtificialData( final Context context )
ellipsoids.put( spot, ellipsoid );
drawSpot( image, interval, ellipsoid );
}
SharedBigDataViewerData sharedBDVData = asSharedBdvDataXyz( image );
SelectionModel< Spot, Link > selectionModel = new DefaultSelectionModel<>( model.getGraph(), model.getGraphIdBimap() );
minimalProjectModel = new MinimalProjectModel( model, sharedBDVData, selectionModel );
minimalProjectModel = DemoUtils.wrapAsMinimalModel( image, model );
selectAllVerticies();
}

Expand All @@ -111,12 +100,6 @@ private static void drawSpot( final Img< FloatType > image, final Interval inter
crop );
}

private static SharedBigDataViewerData asSharedBdvDataXyz( final Img< FloatType > image1 )
{
final ImagePlus image = ImgToVirtualStack.wrap( new ImgPlus<>( image1, "image", new AxisType[] { Axes.X, Axes.Y, Axes.Z } ) );
return Objects.requireNonNull( SharedBigDataViewerData.fromImagePlus( image ) );
}

private void selectAllVerticies()
{
for ( final Spot vertex : minimalProjectModel.getModel().getGraph().vertices() )
Expand Down
54 changes: 54 additions & 0 deletions src/test/java/org/mastodon/mamut/fitting/util/DemoUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.mastodon.mamut.fitting.util;

import ij.ImagePlus;
import net.imagej.ImgPlus;
import net.imagej.axis.Axes;
import net.imagej.axis.AxisType;
import net.imglib2.img.Img;
import net.imglib2.img.display.imagej.ImgToVirtualStack;
import net.imglib2.type.numeric.RealType;
import net.imglib2.type.numeric.real.FloatType;
import org.mastodon.mamut.ProjectModel;
import org.mastodon.mamut.fitting.MinimalProjectModel;
import org.mastodon.mamut.model.Link;
import org.mastodon.mamut.model.Model;
import org.mastodon.mamut.model.Spot;
import org.mastodon.mamut.views.bdv.MamutViewBdv;
import org.mastodon.model.DefaultSelectionModel;
import org.mastodon.model.SelectionModel;
import org.mastodon.views.bdv.SharedBigDataViewerData;

import javax.annotation.Nonnull;
import java.util.Objects;

public class DemoUtils
{
private DemoUtils()
{
// prevent from instantiation
}

public static ProjectModel wrapAsAppModel( final Img< FloatType > image, final Model model )
{
final SharedBigDataViewerData sharedBigDataViewerData = asSharedBdvDataXyz( image );
return ProjectModel.create( null, model, sharedBigDataViewerData, null );
}

public static MinimalProjectModel wrapAsMinimalModel( final Img< FloatType > image, final Model model )
{
SharedBigDataViewerData sharedBDVData = DemoUtils.asSharedBdvDataXyz( image );
SelectionModel< Spot, Link > selectionModel = new DefaultSelectionModel<>( model.getGraph(), model.getGraphIdBimap() );
return new MinimalProjectModel( model, sharedBDVData, selectionModel );
}

public static SharedBigDataViewerData asSharedBdvDataXyz( final Img< FloatType > image1 )
{
final ImagePlus image = ImgToVirtualStack.wrap( new ImgPlus<>( image1, "image", new AxisType[] { Axes.X, Axes.Y, Axes.Z } ) );
return Objects.requireNonNull( SharedBigDataViewerData.fromImagePlus( image ) );
}

public static < T extends RealType< T > > void showBdvWindow( @Nonnull final ProjectModel appModel )
{
appModel.getWindowManager().createView( MamutViewBdv.class );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
package org.mastodon.mamut.fitting;
package org.mastodon.mamut.fitting.util;

import net.imglib2.RandomAccessibleInterval;
import net.imglib2.loops.LoopBuilder;
Expand Down

0 comments on commit 0f054d0

Please sign in to comment.