Skip to content
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

Fix fit ellipsoid test #12

Merged
merged 6 commits into from
Nov 30, 2023
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.mastodon.mamut.fitting;

import org.mastodon.mamut.ProjectModel;
import org.mastodon.mamut.model.Link;
import org.mastodon.mamut.model.Model;
import org.mastodon.mamut.model.Spot;
import org.mastodon.model.SelectionModel;
import org.mastodon.views.bdv.SharedBigDataViewerData;

/**
* A default implementation of {@link MinimalProjectModel}.
*/
public class DefaultMinimalProjectModel implements MinimalProjectModel
{
private final Model model;

private final SharedBigDataViewerData sharedBdvData;

private final SelectionModel< Spot, Link > selectionModel;

DefaultMinimalProjectModel(
final Model model, final SharedBigDataViewerData sharedBdvData, final SelectionModel< Spot, Link > selectionModel
)
{
this.model = model;
this.sharedBdvData = sharedBdvData;
this.selectionModel = selectionModel;
}

public DefaultMinimalProjectModel( final ProjectModel projectModel )
{
this( projectModel.getModel(), projectModel.getSharedBdvData(), projectModel.getSelectionModel() );
}

@Override
public Model getModel()
{
return model;
}

@Override
public SharedBigDataViewerData getSharedBdvData()
{
return sharedBdvData;
}

@Override
public SelectionModel< Spot, Link > getSelectionModel()
{
return selectionModel;
}
}
21 changes: 13 additions & 8 deletions src/main/java/org/mastodon/mamut/fitting/FitEllipsoidPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -126,17 +126,22 @@ public void getCommandDescriptions( final CommandDescriptions descriptions )

private final AbstractNamedAction fitSelectedVerticesAction;

private ProjectModel projectModel;
private MinimalProjectModel minimalProjectModel;

public FitEllipsoidPlugin()
{
fitSelectedVerticesAction = new RunnableAction( FIT_SELECTED_VERTICES, this::fitSelectedVertices );
}

@Override
public void setAppPluginModel( final ProjectModel model )
public void setAppPluginModel( final ProjectModel projectModel )
{
this.projectModel = model;
this.minimalProjectModel = new DefaultMinimalProjectModel( projectModel );
}

void setHeadlessProjectModel( final MinimalProjectModel minimalProjectModel )
stefanhahmann marked this conversation as resolved.
Show resolved Hide resolved
{
this.minimalProjectModel = minimalProjectModel;
}

@Override
Expand Down Expand Up @@ -164,7 +169,7 @@ void fitSelectedVertices()
{
// TODO: parameters to select which source to act on
final int sourceIndex = 0;
final SourceAndConverter< ? > source = projectModel.getSharedBdvData().getSources().get( sourceIndex );
final SourceAndConverter< ? > source = minimalProjectModel.getSharedBdvData().getSources().get( sourceIndex );
if ( !( source.getSpimSource().getType() instanceof RealType ) )
throw new IllegalArgumentException( "Expected RealType image source" );
process( Cast.unchecked( source ) );
Expand All @@ -179,7 +184,7 @@ void fitSelectedVertices()
@SuppressWarnings( "unused" )
private < T extends RealType< T > > void process( final SourceAndConverter< T > source )
{
final RefSet< Spot > vertices = projectModel.getSelectionModel().getSelectedVertices();
final RefSet< Spot > vertices = minimalProjectModel.getSelectionModel().getSelectedVertices();
if ( vertices.isEmpty() )
System.err.println( "no vertex selected" );

Expand All @@ -193,7 +198,7 @@ private < T extends RealType< T > > void process( final SourceAndConverter< T >
final ArrayList< Spot > threadSafeVertices = asArrayList( vertices );
// NB: RefSet is not thread-safe for iteration.
final int totalTasks = vertices.size();
final ReentrantReadWriteLock.WriteLock writeLock = projectModel.getModel().getGraph().getLock().writeLock();
final ReentrantReadWriteLock.WriteLock writeLock = minimalProjectModel.getModel().getGraph().getLock().writeLock();

Parallelization.getTaskExecutor().forEach( threadSafeVertices, spot -> {
// loop over vertices in parallel using multiple threads
Expand Down Expand Up @@ -253,7 +258,7 @@ private < T extends RealType< T > > void process( final SourceAndConverter< T >

// set undo point if at least one spot was fitted
if ( found.get() > 0 )
projectModel.getModel().setUndoPoint();
minimalProjectModel.getModel().setUndoPoint();
}

private static ArrayList< Spot > asArrayList( final RefSet< Spot > vertices )
Expand Down Expand Up @@ -405,7 +410,7 @@ private void showBdvDebugWindow( final SourceAndConverter< ? > source, final dou
{
final BdvStackSource< FloatType > inputSource =
BdvFunctions.show( input, "FloatType input", Bdv.options().sourceTransform( sourceToGlobal ) );
final ConverterSetups setups = projectModel.getSharedBdvData().getConverterSetups();
final ConverterSetups setups = minimalProjectModel.getSharedBdvData().getConverterSetups();
final ConverterSetup cs = setups.getConverterSetup( source );
final Bounds bounds = setups.getBounds().getBounds( cs );
inputSource.setDisplayRange( cs.getDisplayRangeMin(), cs.getDisplayRangeMax() );
Expand Down
35 changes: 35 additions & 0 deletions src/main/java/org/mastodon/mamut/fitting/MinimalProjectModel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.mastodon.mamut.fitting;

import org.mastodon.mamut.model.Link;
import org.mastodon.mamut.model.Model;
import org.mastodon.mamut.model.Spot;
import org.mastodon.model.SelectionModel;
import org.mastodon.views.bdv.SharedBigDataViewerData;

/**
* A minimal interface to the data model of a Mastodon project.<p>
* This interface facilities testing of the ellipsoid fitting, since it allows to circumvent the fact that the {@link org.mastodon.mamut.ProjectModel} creates GUI components on instantiation.<p>
* It contains accessor methods to parts of the {@link org.mastodon.mamut.ProjectModel} that are safe to be used in a headless way.
*
* @author Stefan Hahmann
*/
public interface MinimalProjectModel
{
/**
* Gets the {@link Model} of the project, i.e. all data tracking related data such as the graph and feature.
* @return the model.
*/
Model getModel();

/**
* Gets the {@link SharedBigDataViewerData} of the project, i.e. the image data.
* @return the shared BDV data.
*/
SharedBigDataViewerData getSharedBdvData();

/**
* Gets the {@link SelectionModel} of the project, i.e. the selection of spots and links.
* @return the selection model.
*/
SelectionModel< Spot, Link > getSelectionModel();
}
28 changes: 17 additions & 11 deletions src/test/java/org/mastodon/mamut/fitting/ArtificialData.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@
import org.mastodon.collection.ref.RefObjectHashMap;
import org.mastodon.mamut.ProjectModel;
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;

Expand All @@ -56,7 +59,7 @@
import net.imglib2.view.Views;

/**
* Renders a grid of ellipsoids in 3D, and wraps the result in a {@link MamutAppModel}.
* Renders a grid of ellipsoids in 3D, and wraps the result in a {@link ProjectModel}.
*/
public class ArtificialData
{
Expand All @@ -69,12 +72,15 @@ public class ArtificialData

private final int numberOfSpots = columns * columns * columns;

private final ProjectModel appModel;
private final Context context;

private final MinimalProjectModel minimalProjectModel;

private final RefObjectMap< Spot, Ellipsoid > ellipsoids;

public ArtificialData( final Context context )
{
this.context = context;
final Model model = new Model();
ellipsoids = new RefObjectHashMap<>( model.getGraph().vertices().getRefPool(), numberOfSpots );
final Img< FloatType > image = ArrayImgs.floats( columns * size, columns * size, columns * size );
Expand All @@ -91,8 +97,9 @@ public ArtificialData( final Context context )
ellipsoids.put( spot, ellipsoid );
drawSpot( image, interval, ellipsoid );
}

appModel = wrapAsAppModel( image, model, context );
SharedBigDataViewerData sharedBDVData = asSharedBdvDataXyz( image );
SelectionModel< Spot, Link > selectionModel = new DefaultSelectionModel<>( model.getGraph(), model.getGraphIdBimap() );
minimalProjectModel = new DefaultMinimalProjectModel( model, sharedBDVData, selectionModel );
selectAllVerticies();
}

Expand All @@ -112,8 +119,8 @@ private static SharedBigDataViewerData asSharedBdvDataXyz( final Img< FloatType

private void selectAllVerticies()
{
for ( final Spot vertex : appModel.getModel().getGraph().vertices() )
appModel.getSelectionModel().setSelected( vertex, true );
for ( final Spot vertex : minimalProjectModel.getModel().getGraph().vertices() )
minimalProjectModel.getSelectionModel().setSelected( vertex, true );
}

private Ellipsoid randomizedEllipsoid( final double[] center )
Expand Down Expand Up @@ -167,15 +174,14 @@ private static AffineTransform3D transposed( final AffineTransform3D transform )
return r;
}

private static ProjectModel wrapAsAppModel( final Img< FloatType > image, final Model model, final Context context )
public ProjectModel getAppModel()
{
final SharedBigDataViewerData sharedBigDataViewerData = asSharedBdvDataXyz( image );
return ProjectModel.create( context, model, sharedBigDataViewerData, null );
return ProjectModel.create( context, minimalProjectModel.getModel(), minimalProjectModel.getSharedBdvData(), null );
}

public ProjectModel getAppModel()
public MinimalProjectModel getHeadlessProjectModel()
stefanhahmann marked this conversation as resolved.
Show resolved Hide resolved
{
return appModel;
return minimalProjectModel;
}

public RefObjectMap< Spot, Ellipsoid> getExpectedEllipsoids()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.junit.Test;
import org.mastodon.collection.RefObjectMap;
import org.mastodon.mamut.fitting.ellipsoid.Ellipsoid;
import org.mastodon.mamut.model.ModelGraph;
import org.mastodon.mamut.model.Spot;
import org.scijava.Context;

Expand All @@ -56,18 +57,20 @@ public void testFitEllipsoidPlugin() {
final ArtificialData data = new ArtificialData( new Context() );
final StopWatch watch = StopWatch.createAndStart();
final FitEllipsoidPlugin plugin = new FitEllipsoidPlugin();
plugin.setAppPluginModel( data.getAppModel() );
plugin.setHeadlessProjectModel( data.getHeadlessProjectModel() );
plugin.fitSelectedVertices();
System.out.println( watch );
final int success = countCorrectEllipsoids( data );
assertEquals( "Not all ellipsoids were fitted correctly.", data.getAppModel().getModel().getGraph().vertices().size(), success );
ModelGraph graph = data.getHeadlessProjectModel().getModel().getGraph();
assertEquals( "Not all ellipsoids were fitted correctly.", graph.vertices().size(), success );
}

private static int countCorrectEllipsoids( final ArtificialData data )
{
int success = 0;
final RefObjectMap< Spot, Ellipsoid > expectedEllipsoids = data.getExpectedEllipsoids();
for( final Spot spot : data.getAppModel().getModel().getGraph().vertices() ) {
for ( final Spot spot : data.getHeadlessProjectModel().getModel().getGraph().vertices() )
{
final Ellipsoid actualEllipsoid = asEllipsoid( spot );
final Ellipsoid expectedEllipsoid = expectedEllipsoids.get( spot );
final boolean equal = isEllipsoidEqual( expectedEllipsoid, actualEllipsoid );
Expand Down
Loading