From 14705881c27b8a422cc91a8a04ad4037642e2eb0 Mon Sep 17 00:00:00 2001 From: Stefan Hahmann Date: Thu, 28 Sep 2023 18:10:54 +0200 Subject: [PATCH 01/23] Import spots from labels (unfinished) --- .../ImportEllipsoidsFromLabelsPlugin.java | 65 ++++++++ .../ImportSpotFromLabelsController.java | 140 ++++++++++++++++++ .../ui/ImportSpotsFromLabelsView.java | 43 ++++++ 3 files changed, 248 insertions(+) create mode 100644 src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportEllipsoidsFromLabelsPlugin.java create mode 100644 src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java create mode 100644 src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportEllipsoidsFromLabelsPlugin.java b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportEllipsoidsFromLabelsPlugin.java new file mode 100644 index 000000000..a5c592e26 --- /dev/null +++ b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportEllipsoidsFromLabelsPlugin.java @@ -0,0 +1,65 @@ +package org.mastodon.mamut.segment; + +import org.mastodon.app.ui.ViewMenuBuilder; +import org.mastodon.mamut.MamutAppModel; +import org.mastodon.mamut.plugin.MamutPlugin; +import org.mastodon.mamut.plugin.MamutPluginAppModel; +import org.mastodon.mamut.segment.ui.ImportSpotsFromLabelsView; +import org.scijava.command.CommandService; +import org.scijava.plugin.Parameter; +import org.scijava.plugin.Plugin; +import org.scijava.ui.behaviour.util.AbstractNamedAction; +import org.scijava.ui.behaviour.util.Actions; +import org.scijava.ui.behaviour.util.RunnableAction; + +import java.util.Collections; +import java.util.List; + +import static org.mastodon.app.ui.ViewMenuBuilder.item; +import static org.mastodon.app.ui.ViewMenuBuilder.menu; + +@SuppressWarnings("unused") +@Plugin(type = MamutPlugin.class) +public class ImportEllipsoidsFromLabelsPlugin implements MamutPlugin +{ + private static final String IMPORT_SPOTS_FROM_LABELS = "Import spots from labels"; + + private static final String[] IMPORT_SPOTS_FROM_LABELS_IMAGE_J_KEYS = { "not mapped" }; + + private final AbstractNamedAction importSpotsFromLabels; + + private MamutAppModel appModel; + + @SuppressWarnings("unused") + @Parameter + private CommandService commandService; + + @SuppressWarnings("unused") + public ImportEllipsoidsFromLabelsPlugin() + { + importSpotsFromLabels = new RunnableAction( IMPORT_SPOTS_FROM_LABELS, this::importSpotsFromLabels ); + } + + @Override + public void setAppPluginModel( MamutPluginAppModel pluginAppModel ) + { + this.appModel = pluginAppModel.getAppModel(); + } + + @Override + public List< ViewMenuBuilder.MenuItem > getMenuItems() + { + return Collections.singletonList( menu( "Plugins", item( IMPORT_SPOTS_FROM_LABELS ) ) ); + } + + @Override + public void installGlobalActions( Actions actions ) + { + actions.namedAction( importSpotsFromLabels, IMPORT_SPOTS_FROM_LABELS_IMAGE_J_KEYS ); + } + + private void importSpotsFromLabels() + { + commandService.run( ImportSpotsFromLabelsView.class, true, "appModel", appModel ); + } +} diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java new file mode 100644 index 000000000..cf94740b6 --- /dev/null +++ b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java @@ -0,0 +1,140 @@ +package org.mastodon.mamut.segment; + +import bdv.viewer.Source; +import mpicbg.spim.data.sequence.TimePoint; +import net.imglib2.Cursor; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.type.numeric.RealType; +import net.imglib2.util.Cast; +import net.imglib2.util.LinAlgHelpers; +import net.imglib2.view.Views; +import org.mastodon.mamut.MamutAppModel; +import org.mastodon.mamut.model.Model; +import org.mastodon.mamut.model.ModelGraph; +import org.mastodon.mamut.model.Spot; +import org.scijava.Context; +import org.scijava.app.StatusService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.invoke.MethodHandles; +import java.util.List; + +public class ImportSpotFromLabelsController +{ + + private static final Logger logger = LoggerFactory.getLogger( MethodHandles.lookup().lookupClass() ); + + private final ModelGraph modelGraph; + + private final List< TimePoint > timePoints; + + private final Source< RealType< ? > > source; + + private final StatusService statusService; + + public ImportSpotFromLabelsController( final MamutAppModel appModel, final Context context, int labelChannelIndex ) + { + // NB: Use the dimensions of the first source and the first time point only without checking if they are equal in other sources and time points. + this( appModel.getModel(), + appModel.getSharedBdvData().getSpimData().getSequenceDescription().getTimePoints().getTimePointsOrdered(), + Cast.unchecked( appModel.getSharedBdvData().getSources().get( labelChannelIndex ).getSpimSource() ), context + ); + } + + protected ImportSpotFromLabelsController( + final Model model, final List< TimePoint > timePoints, final Source< RealType< ? > > source, final Context context + ) + { + this.modelGraph = model.getGraph(); + this.timePoints = timePoints; + this.source = source; + this.statusService = context.service( StatusService.class ); + } + + public void createSpotsFromLabels() + { + Spot spot = modelGraph.addVertex(); + int timepointId = 0; + double[] center = new double[] { 50, 50, 50 }; + double[][] cov = new double[][] { { 400, 20, -10 }, { 20, 200, 30 }, { -10, 30, 100 } }; + spot.init( timepointId, center, cov ); + int numTimepoints = timePoints.size(); + + for ( TimePoint frame : timePoints ) + { + int frameId = frame.getId(); + long[] dimensions = source.getSource( frameId, 0 ).dimensionsAsLongArray(); + final RandomAccessibleInterval< RealType< ? > > img = source.getSource( frameId, 0 ); + for ( int d = 0; d < dimensions.length; d++ ) + logger.debug( "Dimension {}, : {}", d, dimensions[ d ] ); + IterableInterval< RealType< ? > > iterable = Views.iterable( img ); + double[] mean = computeMean( iterable, 42 ); + double[][] coviarance = computeCovariance( iterable, mean, 42 ); + statusService.showProgress( frameId, numTimepoints ); + + } + } + + /** + * Computes the mean position of the pixels whose value equals {@code labelValue}. + */ + private static double[] computeMean( IterableInterval< RealType< ? > > iterable, int labelValue ) + { + logger.debug( "Computing mean of label, {} ", labelValue ); + Cursor< RealType< ? > > cursor = iterable.cursor(); + double[] sum = new double[ 3 ]; + double[] position = new double[ 3 ]; + long counter = 0; + while ( cursor.hasNext() ) + { + counter++; + int pixelValue = ( int ) cursor.next().getRealDouble(); + if ( pixelValue == labelValue ) + { + cursor.localize( position ); + LinAlgHelpers.add( sum, position, sum ); + counter++; + } + } + logger.debug( "Computed mean of label {}. Searched {} pixels.", labelValue, counter ); + LinAlgHelpers.scale( sum, 1. / counter, sum ); + return sum; + } + + /** + * Computes the covariance matrix of the pixels whose value equals {@code labelValue}. + */ + private static double[][] computeCovariance( IterableInterval< RealType< ? > > iterable, double[] mean, int labelValue ) + { + Cursor< RealType< ? > > cursor = iterable.cursor(); + long counter = 0; + double[] position = new double[ 3 ]; + double[][] covariance = new double[ 3 ][ 3 ]; + cursor.reset(); + while ( cursor.hasNext() ) + { + int pixelValue = ( int ) cursor.next().getRealDouble(); + if ( pixelValue == labelValue ) + { + 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( double[][] covariance, double factor ) + { + for ( int i = 0; i < 3; i++ ) + for ( int j = 0; j < 3; j++ ) + covariance[ i ][ j ] *= factor; + } +} diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java new file mode 100644 index 000000000..e69a13639 --- /dev/null +++ b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java @@ -0,0 +1,43 @@ +package org.mastodon.mamut.segment.ui; + +import org.mastodon.mamut.MamutAppModel; +import org.mastodon.mamut.segment.ImportSpotFromLabelsController; +import org.scijava.Context; +import org.scijava.ItemVisibility; +import org.scijava.command.Command; +import org.scijava.plugin.Parameter; +import org.scijava.plugin.Plugin; + +@Plugin(type = Command.class, label = "Import spots from labels") +public class ImportSpotsFromLabelsView implements Command +{ + private static final int WIDTH = 15; + + @SuppressWarnings("all") + @Parameter(visibility = ItemVisibility.MESSAGE, required = false, persist = false) + private String documentation = "\n" + + "\n" + + "

Import spots from label image

\n" + + "

This plugin is capable importing spots from a label image.

\n" + + "\n" + + "\n"; + + @SuppressWarnings("unused") + @Parameter + private MamutAppModel appModel; + + @SuppressWarnings("unused") + @Parameter + private Context context; + + @Parameter(label = "Channel index of labels", persist = true) + private int labelChannelIndex = 0; + + @Override + public void run() + { + ImportSpotFromLabelsController controller = new ImportSpotFromLabelsController( appModel, context, labelChannelIndex ); + controller.createSpotsFromLabels(); + } + +} From 0e8fc7be2e49a7107022e07ab2a66bf7e62d8a13 Mon Sep 17 00:00:00 2001 From: Stefan Hahmann Date: Fri, 29 Sep 2023 11:22:14 +0200 Subject: [PATCH 02/23] Set channel to min 0 --- .../io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java index e69a13639..e96aa37e4 100644 --- a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java +++ b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java @@ -30,7 +30,7 @@ public class ImportSpotsFromLabelsView implements Command @Parameter private Context context; - @Parameter(label = "Channel index of labels", persist = true) + @Parameter(label = "Channel index of labels", min = "0") private int labelChannelIndex = 0; @Override From e6642ac569a1714724abec806d9cddb847cabe4e Mon Sep 17 00:00:00 2001 From: Noam Dori Date: Fri, 29 Sep 2023 16:45:58 +0200 Subject: [PATCH 03/23] implemented proper algorithm for finding the spots efficiently from all label IDs. --- .../ImportSpotFromLabelsController.java | 130 ++++++++++-------- 1 file changed, 71 insertions(+), 59 deletions(-) diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java index cf94740b6..e2f82d6f3 100644 --- a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java +++ b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java @@ -3,16 +3,14 @@ import bdv.viewer.Source; import mpicbg.spim.data.sequence.TimePoint; import net.imglib2.Cursor; -import net.imglib2.IterableInterval; import net.imglib2.RandomAccessibleInterval; import net.imglib2.type.numeric.RealType; import net.imglib2.util.Cast; -import net.imglib2.util.LinAlgHelpers; import net.imglib2.view.Views; +import org.jetbrains.annotations.NotNull; import org.mastodon.mamut.MamutAppModel; import org.mastodon.mamut.model.Model; import org.mastodon.mamut.model.ModelGraph; -import org.mastodon.mamut.model.Spot; import org.scijava.Context; import org.scijava.app.StatusService; import org.slf4j.Logger; @@ -55,11 +53,6 @@ protected ImportSpotFromLabelsController( public void createSpotsFromLabels() { - Spot spot = modelGraph.addVertex(); - int timepointId = 0; - double[] center = new double[] { 50, 50, 50 }; - double[][] cov = new double[][] { { 400, 20, -10 }, { 20, 200, 30 }, { -10, 30, 100 } }; - spot.init( timepointId, center, cov ); int numTimepoints = timePoints.size(); for ( TimePoint frame : timePoints ) @@ -69,72 +62,91 @@ public void createSpotsFromLabels() final RandomAccessibleInterval< RealType< ? > > img = source.getSource( frameId, 0 ); for ( int d = 0; d < dimensions.length; d++ ) logger.debug( "Dimension {}, : {}", d, dimensions[ d ] ); - IterableInterval< RealType< ? > > iterable = Views.iterable( img ); - double[] mean = computeMean( iterable, 42 ); - double[][] coviarance = computeCovariance( iterable, mean, 42 ); + + createSpotsFromLabelImage(img, frameId); statusService.showProgress( frameId, numTimepoints ); } } + private void createSpotsFromLabelImage(@NotNull RandomAccessibleInterval> img, int timepointId) { + logger.debug("Computing mean, covariance of all labels at time-point t={}", timepointId); + + // get the maximum value possible to learn how many objects need to be instantiated + // this is fine because we expect maximum occupancy here. + int max = (int) img.randomAccess().setPositionAndGet(img.maxAsPoint()).getRealDouble(); + + int[] count = new int[max]; // counts the number of pixels in each label, for normalization + int[][] sum = new int[max][3]; // sums up the positions of the label pixels, used for the 1D means + int[][][] mixedSum = new int[max][3][3]; // sums up the estimates of mixed coordinates (like xy). Used for covariances. + + readImageSumPositions(img, count, sum, mixedSum); + + createSpotsFromSums(timepointId, max, count, sum, mixedSum); + } + /** - * Computes the mean position of the pixels whose value equals {@code labelValue}. + * Use the gathered information to generate all the spots for the given timepoint. + * @param timepointId the timepoint of the image the spots should belong to. + * @param max the maximum value encountered in the image. Also equal to the number of labels. + * @param count the 0D sums (counts). Dimensions: [labelIdx]. + * @param sum the 1D sums, i.e S[X]. Dimensions: [labelIdx, coord] + * @param mixedSum the 2D sums, i.e S[XY]. Dimensions: [labelIdx, coord, coord] + * @implNote The covariance formula used here is not the definition COV(X,Y) = E[(X - E[X])(Y - E[Y])] + * but instead its simplification COV(X, Y) = E[XY] - E[X]E[Y]. + * Read more here. + * Previously there was a factor of 5 placed on the covariance. + * I removed it, but it might be neccesary for some reason. + * @author Noam Dori */ - private static double[] computeMean( IterableInterval< RealType< ? > > iterable, int labelValue ) - { - logger.debug( "Computing mean of label, {} ", labelValue ); - Cursor< RealType< ? > > cursor = iterable.cursor(); - double[] sum = new double[ 3 ]; - double[] position = new double[ 3 ]; - long counter = 0; - while ( cursor.hasNext() ) - { - counter++; - int pixelValue = ( int ) cursor.next().getRealDouble(); - if ( pixelValue == labelValue ) - { - cursor.localize( position ); - LinAlgHelpers.add( sum, position, sum ); - counter++; + private void createSpotsFromSums(int timepointId, int max, int[] count, int[][] sum, int[][][] mixedSum) { + // combine the sums into mean and covariance matrices, then add the corresponding spot + logger.debug("adding spots for the {} labels found", max); + double[] mean = new double[3]; + double[][] cov = new double[3][3]; + for (int labelIdx = 0; labelIdx < max; labelIdx++) { + for (int i = 0; i < 3; i++) { + mean[i] = sum[labelIdx][i] / (double) count[labelIdx]; + for (int j = i; j < 3; j++) { // the covariance matrix is symmetric! + cov[i][j] = (mixedSum[labelIdx][i][j] - sum[labelIdx][i] * sum[labelIdx][j]) / + (double) count[labelIdx]; // * 5.? + if (i != j) { + cov[j][i] = cov[i][j]; + } + } } + modelGraph.addVertex().init(timepointId, mean, cov); } - logger.debug( "Computed mean of label {}. Searched {} pixels.", labelValue, counter ); - LinAlgHelpers.scale( sum, 1. / counter, sum ); - return sum; } /** - * Computes the covariance matrix of the pixels whose value equals {@code labelValue}. + * Reads the image and prepares the coordinates of all labels to obtain the 0D (count), 1D (sums), and 2D (mixed) + * sums to prep the ground for the mean and covariance estimates. + * @param img the pointer to the image to read. + * @param count an empty array to store the 0D sums (counts). Dimensions: [labelIdx]. + * @param sum an empty array to store the 1D sums, i.e S[X]. Dimensions: [labelIdx, coord] + * @param mixedSum an empty array to store the 2D sums, i.e S[XY]. Dimensions: [labelIdx, coord, coord] + * @author Noam Dori */ - private static double[][] computeCovariance( IterableInterval< RealType< ? > > iterable, double[] mean, int labelValue ) - { - Cursor< RealType< ? > > cursor = iterable.cursor(); - long counter = 0; - double[] position = new double[ 3 ]; - double[][] covariance = new double[ 3 ][ 3 ]; - cursor.reset(); - while ( cursor.hasNext() ) + private static void readImageSumPositions(RandomAccessibleInterval> img, int[] count, + int[][] sum, int[][][] mixedSum) { + // read the picture to sum everything up + int[] position = new int[3]; + Cursor> cursor = Views.iterable(img).cursor(); + while (cursor.hasNext()) { - int pixelValue = ( int ) cursor.next().getRealDouble(); - if ( pixelValue == labelValue ) - { - 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++; + int labelIdx = (int) cursor.next().getRealDouble() - 1; // we ignore 0 as it is BG + if (labelIdx < 0) { + continue; + } + cursor.localize(position); + count[labelIdx]++; + for (int i = 0; i < 3; i++) { + sum[labelIdx][i] += position[i]; + for (int j = i; j < 3; j++) { // the covariance matrix is symmetric! + mixedSum[labelIdx][i][j] += position[i] * position[j]; + } } } - - scale( covariance, 5. / counter ); // I don't know why the factor 5 is needed. But it works. - return covariance; - } - - private static void scale( double[][] covariance, double factor ) - { - for ( int i = 0; i < 3; i++ ) - for ( int j = 0; j < 3; j++ ) - covariance[ i ][ j ] *= factor; } } From 9354b81c01ebd43afe32072061c2853230cb60a3 Mon Sep 17 00:00:00 2001 From: Noam Dori Date: Wed, 4 Oct 2023 17:08:54 +0200 Subject: [PATCH 04/23] Fixed a bunch of bugs relating to data size incompatibility during summation (might need more robust fix later), anisotropic dimensions, finding minimum and max pixel value to account for unsigned conversions,and fixed a bug in the covariance math. also, added support for testing, configuration of the sigma parameter, and directly reading integer images rather than cast them to double. --- .../ImportSpotFromLabelsController.java | 97 +++++++++++++------ .../ui/ImportSpotsFromLabelsView.java | 5 +- .../ImportSpotFromLabelsControllerTest.java | 71 ++++++++++++++ 3 files changed, 144 insertions(+), 29 deletions(-) create mode 100644 src/test/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsControllerTest.java diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java index e2f82d6f3..93ace2f24 100644 --- a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java +++ b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java @@ -2,11 +2,16 @@ import bdv.viewer.Source; import mpicbg.spim.data.sequence.TimePoint; +import mpicbg.spim.data.sequence.VoxelDimensions; import net.imglib2.Cursor; import net.imglib2.RandomAccessibleInterval; +import net.imglib2.type.numeric.IntegerType; import net.imglib2.type.numeric.RealType; import net.imglib2.util.Cast; +import net.imglib2.util.Pair; +import net.imglib2.util.ValuePair; import net.imglib2.view.Views; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.mastodon.mamut.MamutAppModel; import org.mastodon.mamut.model.Model; @@ -28,27 +33,32 @@ public class ImportSpotFromLabelsController private final List< TimePoint > timePoints; - private final Source< RealType< ? > > source; + private final Source< ? extends RealType< ? > > source; private final StatusService statusService; - public ImportSpotFromLabelsController( final MamutAppModel appModel, final Context context, int labelChannelIndex ) + private final VoxelDimensions voxelDimensions; + private final double sigma; + + public ImportSpotFromLabelsController( final MamutAppModel appModel, final Context context, int labelChannelIndex, double sigma ) { // NB: Use the dimensions of the first source and the first time point only without checking if they are equal in other sources and time points. this( appModel.getModel(), appModel.getSharedBdvData().getSpimData().getSequenceDescription().getTimePoints().getTimePointsOrdered(), - Cast.unchecked( appModel.getSharedBdvData().getSources().get( labelChannelIndex ).getSpimSource() ), context - ); + Cast.unchecked( appModel.getSharedBdvData().getSources().get( labelChannelIndex ).getSpimSource() ), context, + appModel.getSharedBdvData().getSpimData().getSequenceDescription().getViewSetups().get( 0 ).getVoxelSize(), sigma); } protected ImportSpotFromLabelsController( - final Model model, final List< TimePoint > timePoints, final Source< RealType< ? > > source, final Context context - ) + final Model model, final List< TimePoint > timePoints, final Source< ? extends RealType< ? > > source, final Context context, + VoxelDimensions voxelDimensions, double sigma) { this.modelGraph = model.getGraph(); this.timePoints = timePoints; this.source = source; - this.statusService = context.service( StatusService.class ); + this.statusService = context.getService( StatusService.class ); + this.voxelDimensions = voxelDimensions; + this.sigma = sigma; } public void createSpotsFromLabels() @@ -59,36 +69,65 @@ public void createSpotsFromLabels() { int frameId = frame.getId(); long[] dimensions = source.getSource( frameId, 0 ).dimensionsAsLongArray(); - final RandomAccessibleInterval< RealType< ? > > img = source.getSource( frameId, 0 ); + final RandomAccessibleInterval< IntegerType< ? > > img = Cast.unchecked(source.getSource( frameId, 0 )); for ( int d = 0; d < dimensions.length; d++ ) logger.debug( "Dimension {}, : {}", d, dimensions[ d ] ); createSpotsFromLabelImage(img, frameId); - statusService.showProgress( frameId, numTimepoints ); + if (statusService != null) { + statusService.showProgress(frameId + 1, numTimepoints); + } } } - private void createSpotsFromLabelImage(@NotNull RandomAccessibleInterval> img, int timepointId) { + private void createSpotsFromLabelImage(@NotNull RandomAccessibleInterval> img, int timepointId) { logger.debug("Computing mean, covariance of all labels at time-point t={}", timepointId); // get the maximum value possible to learn how many objects need to be instantiated // this is fine because we expect maximum occupancy here. - int max = (int) img.randomAccess().setPositionAndGet(img.maxAsPoint()).getRealDouble(); + // we also subtract the background to truly get the number of elements. + Pair minAndMax = getPixelValueInterval(img); - int[] count = new int[max]; // counts the number of pixels in each label, for normalization - int[][] sum = new int[max][3]; // sums up the positions of the label pixels, used for the 1D means - int[][][] mixedSum = new int[max][3][3]; // sums up the estimates of mixed coordinates (like xy). Used for covariances. + int numLabels = minAndMax.getB() - minAndMax.getA(); + int[] count = new int[numLabels]; // counts the number of pixels in each label, for normalization + long[][] sum = new long[numLabels][3]; // sums up the positions of the label pixels, used for the 1D means + long[][][] mixedSum = new long[numLabels][3][3]; // sums up the estimates of mixed coordinates (like xy). Used for covariances. - readImageSumPositions(img, count, sum, mixedSum); + readImageSumPositions(img, count, sum, mixedSum, minAndMax.getA()); + + createSpotsFromSums(timepointId, numLabels, count, sum, mixedSum); + } - createSpotsFromSums(timepointId, max, count, sum, mixedSum); + /** + * Read the image and get its maximum and minimum values + * @param img an image to read and process + * @return A pair of values (min, max) that represent the minimum and maximum pixel values in the image + * @author Noam Dori + */ + @Contract("_ -> new") + private static @NotNull Pair getPixelValueInterval(RandomAccessibleInterval> img) { + // read the picture to sum everything up + int min = Integer.MAX_VALUE; + int max = Integer.MIN_VALUE; + Cursor> cursor = Views.iterable(img).cursor(); + while (cursor.hasNext()) + { + int val = cursor.next().getInteger(); // we ignore 0 as it is BG + if (min > val) { + min = val; + } + if (max < val) { + max = val; + } + } + return new ValuePair<>(min, max); } /** * Use the gathered information to generate all the spots for the given timepoint. * @param timepointId the timepoint of the image the spots should belong to. - * @param max the maximum value encountered in the image. Also equal to the number of labels. + * @param numLabels the maximum value encountered in the image. Also equal to the number of labels. * @param count the 0D sums (counts). Dimensions: [labelIdx]. * @param sum the 1D sums, i.e S[X]. Dimensions: [labelIdx, coord] * @param mixedSum the 2D sums, i.e S[XY]. Dimensions: [labelIdx, coord, coord] @@ -99,17 +138,18 @@ private void createSpotsFromLabelImage(@NotNull RandomAccessibleInterval> img, int[] count, - int[][] sum, int[][][] mixedSum) { + private static void readImageSumPositions(RandomAccessibleInterval> img, int[] count, + long[][] sum, long[][][] mixedSum, int bg) { // read the picture to sum everything up int[] position = new int[3]; - Cursor> cursor = Views.iterable(img).cursor(); + Cursor> cursor = Views.iterable(img).cursor(); while (cursor.hasNext()) { - int labelIdx = (int) cursor.next().getRealDouble() - 1; // we ignore 0 as it is BG + int labelIdx = cursor.next().getInteger() - bg - 1; // we ignore 0 as it is BG if (labelIdx < 0) { continue; } @@ -144,7 +185,7 @@ private static void readImageSumPositions(RandomAccessibleInterval> for (int i = 0; i < 3; i++) { sum[labelIdx][i] += position[i]; for (int j = i; j < 3; j++) { // the covariance matrix is symmetric! - mixedSum[labelIdx][i][j] += position[i] * position[j]; + mixedSum[labelIdx][i][j] += (long)position[i] * position[j]; } } } diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java index e96aa37e4..a9a3021a4 100644 --- a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java +++ b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java @@ -33,10 +33,13 @@ public class ImportSpotsFromLabelsView implements Command @Parameter(label = "Channel index of labels", min = "0") private int labelChannelIndex = 0; + @Parameter(label = "Sigma", min = "0", description = "#deviations from center to form border") + private double sigma = 2.2; + @Override public void run() { - ImportSpotFromLabelsController controller = new ImportSpotFromLabelsController( appModel, context, labelChannelIndex ); + ImportSpotFromLabelsController controller = new ImportSpotFromLabelsController( appModel, context, labelChannelIndex, sigma ); controller.createSpotsFromLabels(); } diff --git a/src/test/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsControllerTest.java b/src/test/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsControllerTest.java new file mode 100644 index 000000000..a512b5277 --- /dev/null +++ b/src/test/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsControllerTest.java @@ -0,0 +1,71 @@ +package org.mastodon.mamut.segment; + +import bdv.util.AbstractSource; +import bdv.util.RandomAccessibleIntervalSource; +import mpicbg.spim.data.sequence.FinalVoxelDimensions; +import mpicbg.spim.data.sequence.TimePoint; +import mpicbg.spim.data.sequence.VoxelDimensions; +import net.imglib2.RandomAccess; +import net.imglib2.img.Img; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.realtransform.AffineTransform3D; +import net.imglib2.type.numeric.integer.IntType; +import org.junit.Before; +import org.junit.Test; +import org.mastodon.mamut.model.Model; +import org.mastodon.mamut.model.Spot; +import org.mastodon.mamut.model.branch.ModelBranchGraph; +import org.scijava.Context; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import static org.junit.Assert.assertTrue; + +public class ImportSpotFromLabelsControllerTest +{ + private Model model; + + private int timepoint; + + @Before + public void setUp() + { + model = new Model(); + ModelBranchGraph modelBranchGraph = model.getBranchGraph(); + modelBranchGraph.graphRebuilt(); + timepoint = 0; + } + + @Test + public void testGetEllipsoidFromImage() { + AbstractSource img = createImage(); + + Context context = new Context(true); + TimePoint timePoint = new TimePoint( timepoint ); + List< TimePoint > timePoints = Collections.singletonList( timePoint ); + VoxelDimensions voxelDimensions = new FinalVoxelDimensions("um", 0.16, 0.16, 1); + ImportSpotFromLabelsController controller = new ImportSpotFromLabelsController(model, timePoints, img, context, voxelDimensions, 2.2); + + controller.createSpotsFromLabels(); + + Iterator iter = model.getGraph().vertices().iterator(); + assertTrue(iter.hasNext()); + + Spot s = iter.next(); + + s.getDoublePosition(0); + } + + private static AbstractSource< IntType > createImage() + { + Img img = new ArrayImgFactory<>(new IntType()).create(4, 4, 4); + RandomAccess ra = img.randomAccess(); + ra.setPositionAndGet(1, 1, 1).set(1); + ra.setPositionAndGet(2, 2, 2).set(1); + ra.setPositionAndGet(3, 3, 3).set(1); + + return new RandomAccessibleIntervalSource<>( img, new IntType(), new AffineTransform3D(), "Segmentation" ); + } +} From cbc5e513b048c6b74f926587fc5a43a1ce157ddd Mon Sep 17 00:00:00 2001 From: Noam Dori Date: Fri, 6 Oct 2023 18:16:08 +0200 Subject: [PATCH 05/23] Fixed accuracy via order of division and fixed overflow bug by introducing BigInteger. We have no way to avoid this as we needed a 128bit integer to truly solve the problem. --- .../ImportSpotFromLabelsController.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java index 93ace2f24..0e41e2c3e 100644 --- a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java +++ b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java @@ -22,8 +22,11 @@ import org.slf4j.LoggerFactory; import java.lang.invoke.MethodHandles; +import java.math.BigInteger; import java.util.List; +import static java.math.BigInteger.valueOf; + public class ImportSpotFromLabelsController { @@ -92,7 +95,7 @@ private void createSpotsFromLabelImage(@NotNull RandomAccessibleInterval> img, int[] count, - long[][] sum, long[][][] mixedSum, int bg) { + long[][] sum, BigInteger[][][] mixedSum, int bg) { // read the picture to sum everything up int[] position = new int[3]; Cursor> cursor = Views.iterable(img).cursor(); @@ -185,7 +189,8 @@ private static void readImageSumPositions(RandomAccessibleInterval Date: Mon, 9 Oct 2023 13:55:36 +0200 Subject: [PATCH 06/23] Apply project code formatter to ImportSpots classes --- .../ImportSpotFromLabelsController.java | 115 ++++++++++-------- .../ui/ImportSpotsFromLabelsView.java | 3 +- .../ImportSpotFromLabelsControllerTest.java | 28 +++-- 3 files changed, 84 insertions(+), 62 deletions(-) diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java index 0e41e2c3e..a6a82c56a 100644 --- a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java +++ b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java @@ -49,12 +49,14 @@ public ImportSpotFromLabelsController( final MamutAppModel appModel, final Conte this( appModel.getModel(), appModel.getSharedBdvData().getSpimData().getSequenceDescription().getTimePoints().getTimePointsOrdered(), Cast.unchecked( appModel.getSharedBdvData().getSources().get( labelChannelIndex ).getSpimSource() ), context, - appModel.getSharedBdvData().getSpimData().getSequenceDescription().getViewSetups().get( 0 ).getVoxelSize(), sigma); + appModel.getSharedBdvData().getSpimData().getSequenceDescription().getViewSetups().get( 0 ).getVoxelSize(), sigma + ); } protected ImportSpotFromLabelsController( final Model model, final List< TimePoint > timePoints, final Source< ? extends RealType< ? > > source, final Context context, - VoxelDimensions voxelDimensions, double sigma) + VoxelDimensions voxelDimensions, double sigma + ) { this.modelGraph = model.getGraph(); this.timePoints = timePoints; @@ -72,34 +74,37 @@ public void createSpotsFromLabels() { int frameId = frame.getId(); long[] dimensions = source.getSource( frameId, 0 ).dimensionsAsLongArray(); - final RandomAccessibleInterval< IntegerType< ? > > img = Cast.unchecked(source.getSource( frameId, 0 )); + final RandomAccessibleInterval< IntegerType< ? > > img = Cast.unchecked( source.getSource( frameId, 0 ) ); for ( int d = 0; d < dimensions.length; d++ ) logger.debug( "Dimension {}, : {}", d, dimensions[ d ] ); - createSpotsFromLabelImage(img, frameId); - if (statusService != null) { - statusService.showProgress(frameId + 1, numTimepoints); + createSpotsFromLabelImage( img, frameId ); + if ( statusService != null ) + { + statusService.showProgress( frameId + 1, numTimepoints ); } } } - private void createSpotsFromLabelImage(@NotNull RandomAccessibleInterval> img, int timepointId) { - logger.debug("Computing mean, covariance of all labels at time-point t={}", timepointId); + private void createSpotsFromLabelImage( @NotNull RandomAccessibleInterval< IntegerType< ? > > img, int timepointId ) + { + logger.debug( "Computing mean, covariance of all labels at time-point t={}", timepointId ); // get the maximum value possible to learn how many objects need to be instantiated // this is fine because we expect maximum occupancy here. // we also subtract the background to truly get the number of elements. - Pair minAndMax = getPixelValueInterval(img); + Pair< Integer, Integer > minAndMax = getPixelValueInterval( img ); int numLabels = minAndMax.getB() - minAndMax.getA(); - int[] count = new int[numLabels]; // counts the number of pixels in each label, for normalization - long[][] sum = new long[numLabels][3]; // sums up the positions of the label pixels, used for the 1D means - BigInteger[][][] mixedSum = new BigInteger[numLabels][3][3]; // sums up the estimates of mixed coordinates (like xy). Used for covariances. + int[] count = new int[ numLabels ]; // counts the number of pixels in each label, for normalization + long[][] sum = new long[ numLabels ][ 3 ]; // sums up the positions of the label pixels, used for the 1D means + BigInteger[][][] mixedSum = + new BigInteger[ numLabels ][ 3 ][ 3 ]; // sums up the estimates of mixed coordinates (like xy). Used for covariances. - readImageSumPositions(img, count, sum, mixedSum, minAndMax.getA()); + readImageSumPositions( img, count, sum, mixedSum, minAndMax.getA() ); - createSpotsFromSums(timepointId, numLabels, count, sum, mixedSum); + createSpotsFromSums( timepointId, numLabels, count, sum, mixedSum ); } /** @@ -109,22 +114,25 @@ private void createSpotsFromLabelImage(@NotNull RandomAccessibleInterval new") - private static @NotNull Pair getPixelValueInterval(RandomAccessibleInterval> img) { + private static @NotNull Pair< Integer, Integer > getPixelValueInterval( RandomAccessibleInterval< IntegerType< ? > > img ) + { // read the picture to sum everything up int min = Integer.MAX_VALUE; int max = Integer.MIN_VALUE; - Cursor> cursor = Views.iterable(img).cursor(); - while (cursor.hasNext()) + Cursor< IntegerType< ? > > cursor = Views.iterable( img ).cursor(); + while ( cursor.hasNext() ) { int val = cursor.next().getInteger(); // we ignore 0 as it is BG - if (min > val) { + if ( min > val ) + { min = val; } - if (max < val) { + if ( max < val ) + { max = val; } } - return new ValuePair<>(min, max); + return new ValuePair<>( min, max ); } /** @@ -141,25 +149,30 @@ private void createSpotsFromLabelImage(@NotNull RandomAccessibleInterval> img, int[] count, - long[][] sum, BigInteger[][][] mixedSum, int bg) { + private static void readImageSumPositions( + RandomAccessibleInterval< IntegerType< ? > > img, int[] count, + long[][] sum, BigInteger[][][] mixedSum, int bg + ) + { // read the picture to sum everything up - int[] position = new int[3]; - Cursor> cursor = Views.iterable(img).cursor(); - while (cursor.hasNext()) + int[] position = new int[ 3 ]; + Cursor< IntegerType< ? > > cursor = Views.iterable( img ).cursor(); + while ( cursor.hasNext() ) { int labelIdx = cursor.next().getInteger() - bg - 1; // we ignore 0 as it is BG - if (labelIdx < 0) { + if ( labelIdx < 0 ) + { continue; } - cursor.localize(position); - count[labelIdx]++; - for (int i = 0; i < 3; i++) { - sum[labelIdx][i] += position[i]; - for (int j = i; j < 3; j++) { // the covariance matrix is symmetric! - mixedSum[labelIdx][i][j] = - mixedSum[labelIdx][i][j].add(valueOf(position[i]).multiply(valueOf(position[j]))); + cursor.localize( position ); + count[ labelIdx ]++; + for ( int i = 0; i < 3; i++ ) + { + sum[ labelIdx ][ i ] += position[ i ]; + for ( int j = i; j < 3; j++ ) + { // the covariance matrix is symmetric! + mixedSum[ labelIdx ][ i ][ j ] = + mixedSum[ labelIdx ][ i ][ j ].add( valueOf( position[ i ] ).multiply( valueOf( position[ j ] ) ) ); } } } diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java index a9a3021a4..b7971822d 100644 --- a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java +++ b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java @@ -39,7 +39,8 @@ public class ImportSpotsFromLabelsView implements Command @Override public void run() { - ImportSpotFromLabelsController controller = new ImportSpotFromLabelsController( appModel, context, labelChannelIndex, sigma ); + ImportSpotFromLabelsController controller = + new ImportSpotFromLabelsController( appModel, context, labelChannelIndex, sigma ); controller.createSpotsFromLabels(); } diff --git a/src/test/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsControllerTest.java b/src/test/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsControllerTest.java index a512b5277..2afb6e5ea 100644 --- a/src/test/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsControllerTest.java +++ b/src/test/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsControllerTest.java @@ -39,32 +39,34 @@ public void setUp() } @Test - public void testGetEllipsoidFromImage() { - AbstractSource img = createImage(); + public void testGetEllipsoidFromImage() + { + AbstractSource< IntType > img = createImage(); - Context context = new Context(true); + Context context = new Context( true ); TimePoint timePoint = new TimePoint( timepoint ); List< TimePoint > timePoints = Collections.singletonList( timePoint ); - VoxelDimensions voxelDimensions = new FinalVoxelDimensions("um", 0.16, 0.16, 1); - ImportSpotFromLabelsController controller = new ImportSpotFromLabelsController(model, timePoints, img, context, voxelDimensions, 2.2); + VoxelDimensions voxelDimensions = new FinalVoxelDimensions( "um", 0.16, 0.16, 1 ); + ImportSpotFromLabelsController controller = + new ImportSpotFromLabelsController( model, timePoints, img, context, voxelDimensions, 2.2 ); controller.createSpotsFromLabels(); - Iterator iter = model.getGraph().vertices().iterator(); - assertTrue(iter.hasNext()); + Iterator< Spot > iter = model.getGraph().vertices().iterator(); + assertTrue( iter.hasNext() ); Spot s = iter.next(); - s.getDoublePosition(0); + s.getDoublePosition( 0 ); } private static AbstractSource< IntType > createImage() { - Img img = new ArrayImgFactory<>(new IntType()).create(4, 4, 4); - RandomAccess ra = img.randomAccess(); - ra.setPositionAndGet(1, 1, 1).set(1); - ra.setPositionAndGet(2, 2, 2).set(1); - ra.setPositionAndGet(3, 3, 3).set(1); + Img< IntType > img = new ArrayImgFactory<>( new IntType() ).create( 4, 4, 4 ); + RandomAccess< IntType > ra = img.randomAccess(); + ra.setPositionAndGet( 1, 1, 1 ).set( 1 ); + ra.setPositionAndGet( 2, 2, 2 ).set( 1 ); + ra.setPositionAndGet( 3, 3, 3 ).set( 1 ); return new RandomAccessibleIntervalSource<>( img, new IntType(), new AffineTransform3D(), "Segmentation" ); } From 99b10a1bde459765ddfb55b76cba769cae3ac280 Mon Sep 17 00:00:00 2001 From: Stefan Hahmann Date: Mon, 9 Oct 2023 13:59:38 +0200 Subject: [PATCH 07/23] Remove jetbrains annotations from ImportSpotsFromLabelImageController --- .../labelimage/ImportSpotFromLabelsController.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java index a6a82c56a..5193676b7 100644 --- a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java +++ b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java @@ -11,8 +11,6 @@ import net.imglib2.util.Pair; import net.imglib2.util.ValuePair; import net.imglib2.view.Views; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; import org.mastodon.mamut.MamutAppModel; import org.mastodon.mamut.model.Model; import org.mastodon.mamut.model.ModelGraph; @@ -87,7 +85,7 @@ public void createSpotsFromLabels() } } - private void createSpotsFromLabelImage( @NotNull RandomAccessibleInterval< IntegerType< ? > > img, int timepointId ) + private void createSpotsFromLabelImage( RandomAccessibleInterval< IntegerType< ? > > img, int timepointId ) { logger.debug( "Computing mean, covariance of all labels at time-point t={}", timepointId ); @@ -113,8 +111,7 @@ private void createSpotsFromLabelImage( @NotNull RandomAccessibleInterval< Integ * @return A pair of values (min, max) that represent the minimum and maximum pixel values in the image * @author Noam Dori */ - @Contract("_ -> new") - private static @NotNull Pair< Integer, Integer > getPixelValueInterval( RandomAccessibleInterval< IntegerType< ? > > img ) + private static Pair< Integer, Integer > getPixelValueInterval( RandomAccessibleInterval< IntegerType< ? > > img ) { // read the picture to sum everything up int min = Integer.MAX_VALUE; From 2a265833982412580f6bd2bc4ad3f67719ed627d Mon Sep 17 00:00:00 2001 From: Stefan Hahmann Date: Mon, 9 Oct 2023 14:00:57 +0200 Subject: [PATCH 08/23] Remove blocks from 1 line if conditions --- .../labelimage/ImportSpotFromLabelsController.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java index 5193676b7..1c14d112d 100644 --- a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java +++ b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java @@ -78,10 +78,7 @@ public void createSpotsFromLabels() createSpotsFromLabelImage( img, frameId ); if ( statusService != null ) - { statusService.showProgress( frameId + 1, numTimepoints ); - } - } } @@ -121,13 +118,9 @@ private static Pair< Integer, Integer > getPixelValueInterval( RandomAccessibleI { int val = cursor.next().getInteger(); // we ignore 0 as it is BG if ( min > val ) - { min = val; - } if ( max < val ) - { max = val; - } } return new ValuePair<>( min, max ); } @@ -164,9 +157,7 @@ private void createSpotsFromSums( int timepointId, int numLabels, int[] count, l .doubleValue() / Math.pow( count[ labelIdx ], 2 ); cov[ i ][ j ] *= Math.pow( sigma, 2 ) * voxelDimensions.dimension( i ) * voxelDimensions.dimension( j ); if ( i != j ) - { cov[ j ][ i ] = cov[ i ][ j ]; - } } } modelGraph.addVertex().init( timepointId, mean, cov ); @@ -195,9 +186,7 @@ private static void readImageSumPositions( { int labelIdx = cursor.next().getInteger() - bg - 1; // we ignore 0 as it is BG if ( labelIdx < 0 ) - { continue; - } cursor.localize( position ); count[ labelIdx ]++; for ( int i = 0; i < 3; i++ ) From 8d2a7a88022fbd45aa2050d843215811fcf330b7 Mon Sep 17 00:00:00 2001 From: Stefan Hahmann Date: Mon, 9 Oct 2023 14:02:59 +0200 Subject: [PATCH 09/23] Suppress warning re final for parameter with default value --- .../io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java index b7971822d..3a99482e8 100644 --- a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java +++ b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java @@ -30,9 +30,11 @@ public class ImportSpotsFromLabelsView implements Command @Parameter private Context context; + @SuppressWarnings("all") @Parameter(label = "Channel index of labels", min = "0") private int labelChannelIndex = 0; + @SuppressWarnings("all") @Parameter(label = "Sigma", min = "0", description = "#deviations from center to form border") private double sigma = 2.2; From e4293ababc84528ca0239d9b9d9f105c6871b6cd Mon Sep 17 00:00:00 2001 From: Stefan Hahmann Date: Mon, 9 Oct 2023 14:25:15 +0200 Subject: [PATCH 10/23] Initialize mixedSum with zero on first use to avoid NullPointerException --- .../labelimage/ImportSpotFromLabelsController.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java index 1c14d112d..17c9e241f 100644 --- a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java +++ b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java @@ -179,23 +179,25 @@ private static void readImageSumPositions( long[][] sum, BigInteger[][][] mixedSum, int bg ) { - // read the picture to sum everything up - int[] position = new int[ 3 ]; + // read all pixels of the picture to sum everything up + int[] pixel = new int[ 3 ]; Cursor< IntegerType< ? > > cursor = Views.iterable( img ).cursor(); while ( cursor.hasNext() ) { int labelIdx = cursor.next().getInteger() - bg - 1; // we ignore 0 as it is BG if ( labelIdx < 0 ) continue; - cursor.localize( position ); + cursor.localize( pixel ); count[ labelIdx ]++; for ( int i = 0; i < 3; i++ ) { - sum[ labelIdx ][ i ] += position[ i ]; + sum[ labelIdx ][ i ] += pixel[ i ]; for ( int j = i; j < 3; j++ ) { // the covariance matrix is symmetric! + if ( mixedSum[ labelIdx ][ i ][ j ] == null ) + mixedSum[ labelIdx ][ i ][ j ] = BigInteger.ZERO; mixedSum[ labelIdx ][ i ][ j ] = - mixedSum[ labelIdx ][ i ][ j ].add( valueOf( position[ i ] ).multiply( valueOf( position[ j ] ) ) ); + mixedSum[ labelIdx ][ i ][ j ].add( valueOf( pixel[ i ] ).multiply( valueOf( pixel[ j ] ) ) ); } } } From 3908dfbfcd57990f80a027b032b0df881f3fba27 Mon Sep 17 00:00:00 2001 From: Stefan Hahmann Date: Mon, 9 Oct 2023 15:19:48 +0200 Subject: [PATCH 11/23] Add final modifier to param VoxelDimensions --- .../exporter/labelimage/ImportSpotFromLabelsController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java index 17c9e241f..f39b51ab9 100644 --- a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java +++ b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java @@ -53,7 +53,7 @@ public ImportSpotFromLabelsController( final MamutAppModel appModel, final Conte protected ImportSpotFromLabelsController( final Model model, final List< TimePoint > timePoints, final Source< ? extends RealType< ? > > source, final Context context, - VoxelDimensions voxelDimensions, double sigma + final VoxelDimensions voxelDimensions, double sigma ) { this.modelGraph = model.getGraph(); @@ -142,7 +142,7 @@ private static Pair< Integer, Integer > getPixelValueInterval( RandomAccessibleI private void createSpotsFromSums( int timepointId, int numLabels, int[] count, long[][] sum, BigInteger[][][] mixedSum ) { // combine the sums into mean and covariance matrices, then add the corresponding spot - logger.debug( "adding spots for the {} labels found", numLabels ); + logger.debug( "Found {} labels. Adding a spot for each label.", numLabels ); double[] mean = new double[ 3 ]; double[][] cov = new double[ 3 ][ 3 ]; for ( int labelIdx = 0; labelIdx < numLabels; labelIdx++ ) From 1c5a7eac569ef9195f2d1676cf7c6f742c2f7ddb Mon Sep 17 00:00:00 2001 From: Stefan Hahmann Date: Mon, 9 Oct 2023 15:21:39 +0200 Subject: [PATCH 12/23] Modified ImportSpotsFromLabelImageControllerTest to contain a mathematically trivial example that can easily be tested. --- .../ImportSpotFromLabelsControllerTest.java | 48 +++++++++++++++---- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/src/test/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsControllerTest.java b/src/test/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsControllerTest.java index 2afb6e5ea..b62ced9e0 100644 --- a/src/test/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsControllerTest.java +++ b/src/test/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsControllerTest.java @@ -15,13 +15,16 @@ import org.mastodon.mamut.model.Model; import org.mastodon.mamut.model.Spot; import org.mastodon.mamut.model.branch.ModelBranchGraph; +import org.mastodon.views.bdv.overlay.util.JamaEigenvalueDecomposition; import org.scijava.Context; import java.util.Collections; import java.util.Iterator; import java.util.List; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; public class ImportSpotFromLabelsControllerTest { @@ -46,27 +49,52 @@ public void testGetEllipsoidFromImage() Context context = new Context( true ); TimePoint timePoint = new TimePoint( timepoint ); List< TimePoint > timePoints = Collections.singletonList( timePoint ); - VoxelDimensions voxelDimensions = new FinalVoxelDimensions( "um", 0.16, 0.16, 1 ); + VoxelDimensions voxelDimensions = new FinalVoxelDimensions( "um", 1, 1, 1 ); ImportSpotFromLabelsController controller = - new ImportSpotFromLabelsController( model, timePoints, img, context, voxelDimensions, 2.2 ); + new ImportSpotFromLabelsController( model, timePoints, img, context, voxelDimensions, 1 ); controller.createSpotsFromLabels(); Iterator< Spot > iter = model.getGraph().vertices().iterator(); - assertTrue( iter.hasNext() ); + Spot spot = iter.next(); + double[][] covarianceMatrix = new double[ 3 ][ 3 ]; + spot.getCovariance( covarianceMatrix ); + final JamaEigenvalueDecomposition eigenvalueDecomposition = new JamaEigenvalueDecomposition( 3 ); + eigenvalueDecomposition.decomposeSymmetric( covarianceMatrix ); + final double[] eigenValues = eigenvalueDecomposition.getRealEigenvalues(); + double axisA = Math.sqrt( eigenValues[ 0 ] ); + double axisB = Math.sqrt( eigenValues[ 1 ] ); + double axisC = Math.sqrt( eigenValues[ 2 ] ); + double radiusSquared = spot.getBoundingSphereRadiusSquared(); - Spot s = iter.next(); - - s.getDoublePosition( 0 ); + assertNotNull( spot ); + assertEquals( 0, spot.getTimepoint() ); + assertEquals( 2, spot.getDoublePosition( 0 ), 0.01 ); + assertEquals( 2, spot.getDoublePosition( 1 ), 0.01 ); + assertEquals( 2, spot.getDoublePosition( 2 ), 0.01 ); + assertEquals( 0, spot.getInternalPoolIndex() ); + assertEquals( "0", spot.getLabel() ); + assertEquals( 1, axisA, 0.01d ); + assertEquals( 1, axisB, 0.01d ); + assertEquals( 1, axisC, 0.01d ); + assertEquals( 1, radiusSquared, 0.01d ); + assertFalse( iter.hasNext() ); } private static AbstractSource< IntType > createImage() { Img< IntType > img = new ArrayImgFactory<>( new IntType() ).create( 4, 4, 4 ); RandomAccess< IntType > ra = img.randomAccess(); - ra.setPositionAndGet( 1, 1, 1 ).set( 1 ); - ra.setPositionAndGet( 2, 2, 2 ).set( 1 ); - ra.setPositionAndGet( 3, 3, 3 ).set( 1 ); + int label = 1; + // 8 corners of a cube + ra.setPositionAndGet( 1, 1, 1 ).set( label ); + ra.setPositionAndGet( 1, 3, 1 ).set( label ); + ra.setPositionAndGet( 3, 1, 1 ).set( label ); + ra.setPositionAndGet( 3, 3, 1 ).set( label ); + ra.setPositionAndGet( 1, 1, 3 ).set( label ); + ra.setPositionAndGet( 1, 3, 3 ).set( label ); + ra.setPositionAndGet( 3, 1, 3 ).set( label ); + ra.setPositionAndGet( 3, 3, 3 ).set( label ); return new RandomAccessibleIntervalSource<>( img, new IntType(), new AffineTransform3D(), "Segmentation" ); } From 32bb3310ae6b647102d3cffc5794227102707643 Mon Sep 17 00:00:00 2001 From: Stefan Hahmann Date: Mon, 9 Oct 2023 15:28:31 +0200 Subject: [PATCH 13/23] Increase code coverage of test by providing a non empty context in the unit test --- .../exporter/labelimage/ImportSpotFromLabelsControllerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsControllerTest.java b/src/test/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsControllerTest.java index b62ced9e0..c59c792ba 100644 --- a/src/test/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsControllerTest.java +++ b/src/test/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsControllerTest.java @@ -46,7 +46,7 @@ public void testGetEllipsoidFromImage() { AbstractSource< IntType > img = createImage(); - Context context = new Context( true ); + Context context = new Context(); TimePoint timePoint = new TimePoint( timepoint ); List< TimePoint > timePoints = Collections.singletonList( timePoint ); VoxelDimensions voxelDimensions = new FinalVoxelDimensions( "um", 1, 1, 1 ); From 3c6366a268566c2432cd657bee19766b1a923dc5 Mon Sep 17 00:00:00 2001 From: Stefan Hahmann Date: Mon, 9 Oct 2023 15:36:21 +0200 Subject: [PATCH 14/23] Rename timepoint variables to frame --- .../ImportSpotFromLabelsController.java | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java index f39b51ab9..8e3d99243 100644 --- a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java +++ b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java @@ -32,13 +32,14 @@ public class ImportSpotFromLabelsController private final ModelGraph modelGraph; - private final List< TimePoint > timePoints; + private final List< TimePoint > frames; private final Source< ? extends RealType< ? > > source; private final StatusService statusService; private final VoxelDimensions voxelDimensions; + private final double sigma; public ImportSpotFromLabelsController( final MamutAppModel appModel, final Context context, int labelChannelIndex, double sigma ) @@ -52,12 +53,12 @@ public ImportSpotFromLabelsController( final MamutAppModel appModel, final Conte } protected ImportSpotFromLabelsController( - final Model model, final List< TimePoint > timePoints, final Source< ? extends RealType< ? > > source, final Context context, + final Model model, final List< TimePoint > frames, final Source< ? extends RealType< ? > > source, final Context context, final VoxelDimensions voxelDimensions, double sigma ) { this.modelGraph = model.getGraph(); - this.timePoints = timePoints; + this.frames = frames; this.source = source; this.statusService = context.getService( StatusService.class ); this.voxelDimensions = voxelDimensions; @@ -66,9 +67,9 @@ protected ImportSpotFromLabelsController( public void createSpotsFromLabels() { - int numTimepoints = timePoints.size(); + int numTimepoints = frames.size(); - for ( TimePoint frame : timePoints ) + for ( TimePoint frame : frames ) { int frameId = frame.getId(); long[] dimensions = source.getSource( frameId, 0 ).dimensionsAsLongArray(); @@ -82,9 +83,9 @@ public void createSpotsFromLabels() } } - private void createSpotsFromLabelImage( RandomAccessibleInterval< IntegerType< ? > > img, int timepointId ) + private void createSpotsFromLabelImage( RandomAccessibleInterval< IntegerType< ? > > img, int frameId ) { - logger.debug( "Computing mean, covariance of all labels at time-point t={}", timepointId ); + logger.debug( "Computing mean, covariance of all labels at frame {}", frameId ); // get the maximum value possible to learn how many objects need to be instantiated // this is fine because we expect maximum occupancy here. @@ -99,7 +100,7 @@ private void createSpotsFromLabelImage( RandomAccessibleInterval< IntegerType< ? readImageSumPositions( img, count, sum, mixedSum, minAndMax.getA() ); - createSpotsFromSums( timepointId, numLabels, count, sum, mixedSum ); + createSpotsFromSums( frameId, numLabels, count, sum, mixedSum ); } /** @@ -127,7 +128,7 @@ private static Pair< Integer, Integer > getPixelValueInterval( RandomAccessibleI /** * Use the gathered information to generate all the spots for the given timepoint. - * @param timepointId the timepoint of the image the spots should belong to. + * @param frame the frame of the image the spots should belong to. * @param numLabels the maximum value encountered in the image. Also equal to the number of labels. * @param count the 0D sums (counts). Dimensions: [labelIdx]. * @param sum the 1D sums, i.e S[X]. Dimensions: [labelIdx, coord] @@ -139,7 +140,7 @@ private static Pair< Integer, Integer > getPixelValueInterval( RandomAccessibleI * I removed it, but it might be neccesary for some reason. * @author Noam Dori */ - private void createSpotsFromSums( int timepointId, int numLabels, int[] count, long[][] sum, BigInteger[][][] mixedSum ) + private void createSpotsFromSums( int frame, int numLabels, int[] count, long[][] sum, BigInteger[][][] mixedSum ) { // combine the sums into mean and covariance matrices, then add the corresponding spot logger.debug( "Found {} labels. Adding a spot for each label.", numLabels ); @@ -160,7 +161,7 @@ private void createSpotsFromSums( int timepointId, int numLabels, int[] count, l cov[ j ][ i ] = cov[ i ][ j ]; } } - modelGraph.addVertex().init( timepointId, mean, cov ); + modelGraph.addVertex().init( frame, mean, cov ); } } From 50f2c5f8eb0465c5819a1d8627f93fa9d7a96459 Mon Sep 17 00:00:00 2001 From: Stefan Hahmann Date: Mon, 9 Oct 2023 15:36:47 +0200 Subject: [PATCH 15/23] Add javadoc to createSpotsFromLabels() method --- .../exporter/labelimage/ImportSpotFromLabelsController.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java index 8e3d99243..7a575c242 100644 --- a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java +++ b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java @@ -65,6 +65,10 @@ protected ImportSpotFromLabelsController( this.sigma = sigma; } + /** + * Converts label images to (spot) ellipsoids.

+ * The method runs twice through each image (i.e. each frame) read. Once to determine maximum/minimum values for array initialization, and once to do summation for covariance & mean. + */ public void createSpotsFromLabels() { int numTimepoints = frames.size(); From 228fe5d44050dfa2f17fc02486d6e949c4d1e38b Mon Sep 17 00:00:00 2001 From: Stefan Hahmann Date: Mon, 9 Oct 2023 15:45:24 +0200 Subject: [PATCH 16/23] Rename bg to background --- .../labelimage/ImportSpotFromLabelsController.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java index 7a575c242..2bf9c0ca9 100644 --- a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java +++ b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java @@ -103,7 +103,6 @@ private void createSpotsFromLabelImage( RandomAccessibleInterval< IntegerType< ? new BigInteger[ numLabels ][ 3 ][ 3 ]; // sums up the estimates of mixed coordinates (like xy). Used for covariances. readImageSumPositions( img, count, sum, mixedSum, minAndMax.getA() ); - createSpotsFromSums( frameId, numLabels, count, sum, mixedSum ); } @@ -121,7 +120,7 @@ private static Pair< Integer, Integer > getPixelValueInterval( RandomAccessibleI Cursor< IntegerType< ? > > cursor = Views.iterable( img ).cursor(); while ( cursor.hasNext() ) { - int val = cursor.next().getInteger(); // we ignore 0 as it is BG + int val = cursor.next().getInteger(); // we ignore 0 as it is background if ( min > val ) min = val; if ( max < val ) @@ -176,12 +175,11 @@ private void createSpotsFromSums( int frame, int numLabels, int[] count, long[][ * @param count an empty array to store the 0D sums (counts). Dimensions: [labelIdx]. * @param sum an empty array to store the 1D sums, i.e S[X]. Dimensions: [labelIdx, coord] * @param mixedSum an empty array to store the 2D sums, i.e S[XY]. Dimensions: [labelIdx, coord, coord] - * @param bg the pixel value of the background. Since unsigned is annoying in Fiji, this subtracts the bg value from the label. + * @param background the pixel value of the background. Since unsigned is annoying in Fiji, this subtracts the bg value from the label. * @author Noam Dori */ private static void readImageSumPositions( - RandomAccessibleInterval< IntegerType< ? > > img, int[] count, - long[][] sum, BigInteger[][][] mixedSum, int bg + RandomAccessibleInterval< IntegerType< ? > > img, int[] count, long[][] sum, BigInteger[][][] mixedSum, int background ) { // read all pixels of the picture to sum everything up @@ -189,7 +187,7 @@ private static void readImageSumPositions( Cursor< IntegerType< ? > > cursor = Views.iterable( img ).cursor(); while ( cursor.hasNext() ) { - int labelIdx = cursor.next().getInteger() - bg - 1; // we ignore 0 as it is BG + int labelIdx = cursor.next().getInteger() - background - 1; // we ignore 0 as it is BG if ( labelIdx < 0 ) continue; cursor.localize( pixel ); From 260ff2ac806b5b78de474e00f2dd7a69714d428d Mon Sep 17 00:00:00 2001 From: Stefan Hahmann Date: Tue, 17 Oct 2023 16:07:47 +0200 Subject: [PATCH 17/23] A possible human-caused error can occur by choosing the incorrect channel to convert labels into ellipsoids (using the raw image instead of the label image) A warning is thus printed if lots of labels are detected (i.e. more than 10_000) --- .../io/exporter/labelimage/ImportSpotFromLabelsController.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java index 2bf9c0ca9..292b900ac 100644 --- a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java +++ b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java @@ -97,6 +97,8 @@ private void createSpotsFromLabelImage( RandomAccessibleInterval< IntegerType< ? Pair< Integer, Integer > minAndMax = getPixelValueInterval( img ); int numLabels = minAndMax.getB() - minAndMax.getA(); + if ( numLabels > 10_000 ) // 10_000 is arbitrary, but we shouldn't expect this many labels from one image + logger.warn( "found {} labels, are you sure you used the correct channel?", numLabels ); int[] count = new int[ numLabels ]; // counts the number of pixels in each label, for normalization long[][] sum = new long[ numLabels ][ 3 ]; // sums up the positions of the label pixels, used for the 1D means BigInteger[][][] mixedSum = From cfa47c4138b7890e6ca2a30956d53645ffbb86f9 Mon Sep 17 00:00:00 2001 From: Stefan Hahmann Date: Fri, 10 Nov 2023 11:06:28 +0100 Subject: [PATCH 18/23] Use ProjectModel instead of MamutAppModel --- .../ImportEllipsoidsFromLabelsPlugin.java | 9 ++++----- .../labelimage/ImportSpotFromLabelsController.java | 14 ++++++++------ .../labelimage/ui/ImportSpotsFromLabelsView.java | 6 +++--- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportEllipsoidsFromLabelsPlugin.java b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportEllipsoidsFromLabelsPlugin.java index a5c592e26..dd0dd699a 100644 --- a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportEllipsoidsFromLabelsPlugin.java +++ b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportEllipsoidsFromLabelsPlugin.java @@ -1,9 +1,8 @@ package org.mastodon.mamut.segment; import org.mastodon.app.ui.ViewMenuBuilder; -import org.mastodon.mamut.MamutAppModel; +import org.mastodon.mamut.ProjectModel; import org.mastodon.mamut.plugin.MamutPlugin; -import org.mastodon.mamut.plugin.MamutPluginAppModel; import org.mastodon.mamut.segment.ui.ImportSpotsFromLabelsView; import org.scijava.command.CommandService; import org.scijava.plugin.Parameter; @@ -28,7 +27,7 @@ public class ImportEllipsoidsFromLabelsPlugin implements MamutPlugin private final AbstractNamedAction importSpotsFromLabels; - private MamutAppModel appModel; + private ProjectModel appModel; @SuppressWarnings("unused") @Parameter @@ -41,9 +40,9 @@ public ImportEllipsoidsFromLabelsPlugin() } @Override - public void setAppPluginModel( MamutPluginAppModel pluginAppModel ) + public void setAppPluginModel( final ProjectModel appPluginModel ) { - this.appModel = pluginAppModel.getAppModel(); + this.appModel = appPluginModel; } @Override diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java index 292b900ac..28bc5edf8 100644 --- a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java +++ b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java @@ -11,7 +11,7 @@ import net.imglib2.util.Pair; import net.imglib2.util.ValuePair; import net.imglib2.view.Views; -import org.mastodon.mamut.MamutAppModel; +import org.mastodon.mamut.ProjectModel; import org.mastodon.mamut.model.Model; import org.mastodon.mamut.model.ModelGraph; import org.scijava.Context; @@ -42,13 +42,15 @@ public class ImportSpotFromLabelsController private final double sigma; - public ImportSpotFromLabelsController( final MamutAppModel appModel, final Context context, int labelChannelIndex, double sigma ) + public ImportSpotFromLabelsController( + final ProjectModel projectModel, final Context context, int labelChannelIndex, double sigma + ) { // NB: Use the dimensions of the first source and the first time point only without checking if they are equal in other sources and time points. - this( appModel.getModel(), - appModel.getSharedBdvData().getSpimData().getSequenceDescription().getTimePoints().getTimePointsOrdered(), - Cast.unchecked( appModel.getSharedBdvData().getSources().get( labelChannelIndex ).getSpimSource() ), context, - appModel.getSharedBdvData().getSpimData().getSequenceDescription().getViewSetups().get( 0 ).getVoxelSize(), sigma + this( projectModel.getModel(), + projectModel.getSharedBdvData().getSpimData().getSequenceDescription().getTimePoints().getTimePointsOrdered(), + Cast.unchecked( projectModel.getSharedBdvData().getSources().get( labelChannelIndex ).getSpimSource() ), context, + projectModel.getSharedBdvData().getSpimData().getSequenceDescription().getViewSetups().get( 0 ).getVoxelSize(), sigma ); } diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java index 3a99482e8..86a453c70 100644 --- a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java +++ b/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java @@ -1,6 +1,6 @@ package org.mastodon.mamut.segment.ui; -import org.mastodon.mamut.MamutAppModel; +import org.mastodon.mamut.ProjectModel; import org.mastodon.mamut.segment.ImportSpotFromLabelsController; import org.scijava.Context; import org.scijava.ItemVisibility; @@ -24,7 +24,7 @@ public class ImportSpotsFromLabelsView implements Command @SuppressWarnings("unused") @Parameter - private MamutAppModel appModel; + private ProjectModel projectModel; @SuppressWarnings("unused") @Parameter @@ -42,7 +42,7 @@ public class ImportSpotsFromLabelsView implements Command public void run() { ImportSpotFromLabelsController controller = - new ImportSpotFromLabelsController( appModel, context, labelChannelIndex, sigma ); + new ImportSpotFromLabelsController( projectModel, context, labelChannelIndex, sigma ); controller.createSpotsFromLabels(); } From 98de35b8f56343c63ab675b9fe1d1eb272eb5759 Mon Sep 17 00:00:00 2001 From: Stefan Hahmann Date: Mon, 13 Nov 2023 15:52:13 +0100 Subject: [PATCH 19/23] Move import spots from label image plugin to org.mastodon.mamut.io.importer.labelimage * These respective plugin is importing instead of exporting --- .../labelimage/ImportEllipsoidsFromLabelsPlugin.java | 4 ++-- .../labelimage/ImportSpotFromLabelsController.java | 2 +- .../labelimage/ui/ImportSpotsFromLabelsView.java | 4 ++-- .../labelimage/ImportSpotFromLabelsControllerTest.java | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) rename src/main/java/org/mastodon/mamut/io/{exporter => importer}/labelimage/ImportEllipsoidsFromLabelsPlugin.java (93%) rename src/main/java/org/mastodon/mamut/io/{exporter => importer}/labelimage/ImportSpotFromLabelsController.java (99%) rename src/main/java/org/mastodon/mamut/io/{exporter => importer}/labelimage/ui/ImportSpotsFromLabelsView.java (91%) rename src/test/java/org/mastodon/mamut/io/{exporter => importer}/labelimage/ImportSpotFromLabelsControllerTest.java (98%) diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportEllipsoidsFromLabelsPlugin.java b/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportEllipsoidsFromLabelsPlugin.java similarity index 93% rename from src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportEllipsoidsFromLabelsPlugin.java rename to src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportEllipsoidsFromLabelsPlugin.java index dd0dd699a..4a20a2173 100644 --- a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportEllipsoidsFromLabelsPlugin.java +++ b/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportEllipsoidsFromLabelsPlugin.java @@ -1,9 +1,9 @@ -package org.mastodon.mamut.segment; +package org.mastodon.mamut.io.importer.labelimage; import org.mastodon.app.ui.ViewMenuBuilder; import org.mastodon.mamut.ProjectModel; import org.mastodon.mamut.plugin.MamutPlugin; -import org.mastodon.mamut.segment.ui.ImportSpotsFromLabelsView; +import org.mastodon.mamut.io.importer.labelimage.ui.ImportSpotsFromLabelsView; import org.scijava.command.CommandService; import org.scijava.plugin.Parameter; import org.scijava.plugin.Plugin; diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java b/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotFromLabelsController.java similarity index 99% rename from src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java rename to src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotFromLabelsController.java index 28bc5edf8..8f9baea1d 100644 --- a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsController.java +++ b/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotFromLabelsController.java @@ -1,4 +1,4 @@ -package org.mastodon.mamut.segment; +package org.mastodon.mamut.io.importer.labelimage; import bdv.viewer.Source; import mpicbg.spim.data.sequence.TimePoint; diff --git a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java b/src/main/java/org/mastodon/mamut/io/importer/labelimage/ui/ImportSpotsFromLabelsView.java similarity index 91% rename from src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java rename to src/main/java/org/mastodon/mamut/io/importer/labelimage/ui/ImportSpotsFromLabelsView.java index 86a453c70..609e03343 100644 --- a/src/main/java/org/mastodon/mamut/io/exporter/labelimage/ui/ImportSpotsFromLabelsView.java +++ b/src/main/java/org/mastodon/mamut/io/importer/labelimage/ui/ImportSpotsFromLabelsView.java @@ -1,7 +1,7 @@ -package org.mastodon.mamut.segment.ui; +package org.mastodon.mamut.io.importer.labelimage.ui; import org.mastodon.mamut.ProjectModel; -import org.mastodon.mamut.segment.ImportSpotFromLabelsController; +import org.mastodon.mamut.io.importer.labelimage.ImportSpotFromLabelsController; import org.scijava.Context; import org.scijava.ItemVisibility; import org.scijava.command.Command; diff --git a/src/test/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsControllerTest.java b/src/test/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotFromLabelsControllerTest.java similarity index 98% rename from src/test/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsControllerTest.java rename to src/test/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotFromLabelsControllerTest.java index c59c792ba..6119aa26c 100644 --- a/src/test/java/org/mastodon/mamut/io/exporter/labelimage/ImportSpotFromLabelsControllerTest.java +++ b/src/test/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotFromLabelsControllerTest.java @@ -1,4 +1,4 @@ -package org.mastodon.mamut.segment; +package org.mastodon.mamut.io.importer.labelimage; import bdv.util.AbstractSource; import bdv.util.RandomAccessibleIntervalSource; From 8a1a72b81e78c80e8acf7daac7b20a1c735f30b3 Mon Sep 17 00:00:00 2001 From: Stefan Hahmann Date: Mon, 13 Nov 2023 15:56:09 +0100 Subject: [PATCH 20/23] Rename classes to ImportSpotsFromLabelImage* --- ... ImportSpotsFromLabelImageController.java} | 8 ++++---- ...a => ImportSpotsFromLabelImagePlugin.java} | 20 +++++++++---------- ...ava => ImportSpotsFromLabelImageView.java} | 12 +++++------ ...ortSpotsFromLabelImageControllerTest.java} | 8 ++++---- 4 files changed, 24 insertions(+), 24 deletions(-) rename src/main/java/org/mastodon/mamut/io/importer/labelimage/{ImportSpotFromLabelsController.java => ImportSpotsFromLabelImageController.java} (97%) rename src/main/java/org/mastodon/mamut/io/importer/labelimage/{ImportEllipsoidsFromLabelsPlugin.java => ImportSpotsFromLabelImagePlugin.java} (68%) rename src/main/java/org/mastodon/mamut/io/importer/labelimage/ui/{ImportSpotsFromLabelsView.java => ImportSpotsFromLabelImageView.java} (73%) rename src/test/java/org/mastodon/mamut/io/importer/labelimage/{ImportSpotFromLabelsControllerTest.java => ImportSpotsFromLabelImageControllerTest.java} (93%) diff --git a/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotFromLabelsController.java b/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotsFromLabelImageController.java similarity index 97% rename from src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotFromLabelsController.java rename to src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotsFromLabelImageController.java index 8f9baea1d..f9d160e0c 100644 --- a/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotFromLabelsController.java +++ b/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotsFromLabelImageController.java @@ -25,7 +25,7 @@ import static java.math.BigInteger.valueOf; -public class ImportSpotFromLabelsController +public class ImportSpotsFromLabelImageController { private static final Logger logger = LoggerFactory.getLogger( MethodHandles.lookup().lookupClass() ); @@ -42,7 +42,7 @@ public class ImportSpotFromLabelsController private final double sigma; - public ImportSpotFromLabelsController( + public ImportSpotsFromLabelImageController( final ProjectModel projectModel, final Context context, int labelChannelIndex, double sigma ) { @@ -54,7 +54,7 @@ public ImportSpotFromLabelsController( ); } - protected ImportSpotFromLabelsController( + protected ImportSpotsFromLabelImageController( final Model model, final List< TimePoint > frames, final Source< ? extends RealType< ? > > source, final Context context, final VoxelDimensions voxelDimensions, double sigma ) @@ -71,7 +71,7 @@ protected ImportSpotFromLabelsController( * Converts label images to (spot) ellipsoids.

* The method runs twice through each image (i.e. each frame) read. Once to determine maximum/minimum values for array initialization, and once to do summation for covariance & mean. */ - public void createSpotsFromLabels() + public void createSpotsFromLabelImage() { int numTimepoints = frames.size(); diff --git a/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportEllipsoidsFromLabelsPlugin.java b/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotsFromLabelImagePlugin.java similarity index 68% rename from src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportEllipsoidsFromLabelsPlugin.java rename to src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotsFromLabelImagePlugin.java index 4a20a2173..0f9370cc0 100644 --- a/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportEllipsoidsFromLabelsPlugin.java +++ b/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotsFromLabelImagePlugin.java @@ -3,7 +3,7 @@ import org.mastodon.app.ui.ViewMenuBuilder; import org.mastodon.mamut.ProjectModel; import org.mastodon.mamut.plugin.MamutPlugin; -import org.mastodon.mamut.io.importer.labelimage.ui.ImportSpotsFromLabelsView; +import org.mastodon.mamut.io.importer.labelimage.ui.ImportSpotsFromLabelImageView; import org.scijava.command.CommandService; import org.scijava.plugin.Parameter; import org.scijava.plugin.Plugin; @@ -19,13 +19,13 @@ @SuppressWarnings("unused") @Plugin(type = MamutPlugin.class) -public class ImportEllipsoidsFromLabelsPlugin implements MamutPlugin +public class ImportSpotsFromLabelImagePlugin implements MamutPlugin { - private static final String IMPORT_SPOTS_FROM_LABELS = "Import spots from labels"; + private static final String IMPORT_SPOTS_FROM_LABEL_IMAGE = "Import spots from label image"; private static final String[] IMPORT_SPOTS_FROM_LABELS_IMAGE_J_KEYS = { "not mapped" }; - private final AbstractNamedAction importSpotsFromLabels; + private final AbstractNamedAction action; private ProjectModel appModel; @@ -34,9 +34,9 @@ public class ImportEllipsoidsFromLabelsPlugin implements MamutPlugin private CommandService commandService; @SuppressWarnings("unused") - public ImportEllipsoidsFromLabelsPlugin() + public ImportSpotsFromLabelImagePlugin() { - importSpotsFromLabels = new RunnableAction( IMPORT_SPOTS_FROM_LABELS, this::importSpotsFromLabels ); + action = new RunnableAction( IMPORT_SPOTS_FROM_LABEL_IMAGE, this::importSpotsFromLabelImage ); } @Override @@ -48,17 +48,17 @@ public void setAppPluginModel( final ProjectModel appPluginModel ) @Override public List< ViewMenuBuilder.MenuItem > getMenuItems() { - return Collections.singletonList( menu( "Plugins", item( IMPORT_SPOTS_FROM_LABELS ) ) ); + return Collections.singletonList( menu( "Plugins", item( IMPORT_SPOTS_FROM_LABEL_IMAGE ) ) ); } @Override public void installGlobalActions( Actions actions ) { - actions.namedAction( importSpotsFromLabels, IMPORT_SPOTS_FROM_LABELS_IMAGE_J_KEYS ); + actions.namedAction( action, IMPORT_SPOTS_FROM_LABELS_IMAGE_J_KEYS ); } - private void importSpotsFromLabels() + private void importSpotsFromLabelImage() { - commandService.run( ImportSpotsFromLabelsView.class, true, "appModel", appModel ); + commandService.run( ImportSpotsFromLabelImageView.class, true, "appModel", appModel ); } } diff --git a/src/main/java/org/mastodon/mamut/io/importer/labelimage/ui/ImportSpotsFromLabelsView.java b/src/main/java/org/mastodon/mamut/io/importer/labelimage/ui/ImportSpotsFromLabelImageView.java similarity index 73% rename from src/main/java/org/mastodon/mamut/io/importer/labelimage/ui/ImportSpotsFromLabelsView.java rename to src/main/java/org/mastodon/mamut/io/importer/labelimage/ui/ImportSpotsFromLabelImageView.java index 609e03343..f12726747 100644 --- a/src/main/java/org/mastodon/mamut/io/importer/labelimage/ui/ImportSpotsFromLabelsView.java +++ b/src/main/java/org/mastodon/mamut/io/importer/labelimage/ui/ImportSpotsFromLabelImageView.java @@ -1,15 +1,15 @@ package org.mastodon.mamut.io.importer.labelimage.ui; import org.mastodon.mamut.ProjectModel; -import org.mastodon.mamut.io.importer.labelimage.ImportSpotFromLabelsController; +import org.mastodon.mamut.io.importer.labelimage.ImportSpotsFromLabelImageController; import org.scijava.Context; import org.scijava.ItemVisibility; import org.scijava.command.Command; import org.scijava.plugin.Parameter; import org.scijava.plugin.Plugin; -@Plugin(type = Command.class, label = "Import spots from labels") -public class ImportSpotsFromLabelsView implements Command +@Plugin(type = Command.class, label = "Import spots from label image") +public class ImportSpotsFromLabelImageView implements Command { private static final int WIDTH = 15; @@ -41,9 +41,9 @@ public class ImportSpotsFromLabelsView implements Command @Override public void run() { - ImportSpotFromLabelsController controller = - new ImportSpotFromLabelsController( projectModel, context, labelChannelIndex, sigma ); - controller.createSpotsFromLabels(); + ImportSpotsFromLabelImageController controller = + new ImportSpotsFromLabelImageController( projectModel, context, labelChannelIndex, sigma ); + controller.createSpotsFromLabelImage(); } } diff --git a/src/test/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotFromLabelsControllerTest.java b/src/test/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotsFromLabelImageControllerTest.java similarity index 93% rename from src/test/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotFromLabelsControllerTest.java rename to src/test/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotsFromLabelImageControllerTest.java index 6119aa26c..12562854b 100644 --- a/src/test/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotFromLabelsControllerTest.java +++ b/src/test/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotsFromLabelImageControllerTest.java @@ -26,7 +26,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -public class ImportSpotFromLabelsControllerTest +public class ImportSpotsFromLabelImageControllerTest { private Model model; @@ -50,10 +50,10 @@ public void testGetEllipsoidFromImage() TimePoint timePoint = new TimePoint( timepoint ); List< TimePoint > timePoints = Collections.singletonList( timePoint ); VoxelDimensions voxelDimensions = new FinalVoxelDimensions( "um", 1, 1, 1 ); - ImportSpotFromLabelsController controller = - new ImportSpotFromLabelsController( model, timePoints, img, context, voxelDimensions, 1 ); + ImportSpotsFromLabelImageController controller = + new ImportSpotsFromLabelImageController( model, timePoints, img, context, voxelDimensions, 1 ); - controller.createSpotsFromLabels(); + controller.createSpotsFromLabelImage(); Iterator< Spot > iter = model.getGraph().vertices().iterator(); Spot spot = iter.next(); From b77583a8e8832023f0a43e4f47a6a3e010b1157b Mon Sep 17 00:00:00 2001 From: Stefan Hahmann Date: Mon, 13 Nov 2023 15:56:56 +0100 Subject: [PATCH 21/23] Move Plugin in menu to Plugins > Imports --- .../io/importer/labelimage/ImportSpotsFromLabelImagePlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotsFromLabelImagePlugin.java b/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotsFromLabelImagePlugin.java index 0f9370cc0..294ca4d84 100644 --- a/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotsFromLabelImagePlugin.java +++ b/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotsFromLabelImagePlugin.java @@ -48,7 +48,7 @@ public void setAppPluginModel( final ProjectModel appPluginModel ) @Override public List< ViewMenuBuilder.MenuItem > getMenuItems() { - return Collections.singletonList( menu( "Plugins", item( IMPORT_SPOTS_FROM_LABEL_IMAGE ) ) ); + return Collections.singletonList( menu( "Plugins", menu( "Imports", item( IMPORT_SPOTS_FROM_LABEL_IMAGE ) ) ) ); } @Override From f8db2a5e105b95b9e83722b6608713aefcb23989 Mon Sep 17 00:00:00 2001 From: Stefan Hahmann Date: Fri, 24 Nov 2023 11:54:43 +0100 Subject: [PATCH 22/23] Fix javadoc related errors --- .../labelimage/ImportSpotsFromLabelImageController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotsFromLabelImageController.java b/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotsFromLabelImageController.java index f9d160e0c..8e46bae4c 100644 --- a/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotsFromLabelImageController.java +++ b/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotsFromLabelImageController.java @@ -68,8 +68,8 @@ protected ImportSpotsFromLabelImageController( } /** - * Converts label images to (spot) ellipsoids.

- * The method runs twice through each image (i.e. each frame) read. Once to determine maximum/minimum values for array initialization, and once to do summation for covariance & mean. + * Converts label images to (spot) ellipsoids.
+ * The method runs twice through each image (i.e. each frame) read. Once to determine maximum/minimum values for array initialization, and once to do summation for covariance and mean. */ public void createSpotsFromLabelImage() { From 4a926fdb741a28b21405cc8d80331c755e88fe53 Mon Sep 17 00:00:00 2001 From: Stefan Hahmann Date: Mon, 26 Feb 2024 13:54:33 +0100 Subject: [PATCH 23/23] Change variable name to projectModel when calling ImportSpotsFromLabelImageView via the CommandService --- .../io/importer/labelimage/ImportSpotsFromLabelImagePlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotsFromLabelImagePlugin.java b/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotsFromLabelImagePlugin.java index 294ca4d84..b85d355f5 100644 --- a/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotsFromLabelImagePlugin.java +++ b/src/main/java/org/mastodon/mamut/io/importer/labelimage/ImportSpotsFromLabelImagePlugin.java @@ -59,6 +59,6 @@ public void installGlobalActions( Actions actions ) private void importSpotsFromLabelImage() { - commandService.run( ImportSpotsFromLabelImageView.class, true, "appModel", appModel ); + commandService.run( ImportSpotsFromLabelImageView.class, true, "projectModel", appModel ); } }