diff --git a/README.md b/README.md index cb01a247..f911a499 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,19 @@ place. #### Systematically label spots (extern-intern) +* Menu Location: `Plugins > Spots management > Rename spots > Systematically label spots (extern-intern)` +* Derives the name of child cells from the name of the parent by appending a "1" or a "2" to the parent cell name. +* The child cell further away from the center landmark gets "1" appended. +* The child cell closer to the center landmark gets "2" appended. +* The command is useful for systematically labeling cells in a lineage tree. +* The renaming can be restricted to + * Selected spots + * Spots with a certain tag + * Spots that are yet labeled with a number only (indicating that they were automatically detected and not yet + manually labeled) + * Spots whose names end with "1" or "2" (indicating that they were labeled with this command before) +* Example: ![systematically_label_spots.gif](doc/spotsmanagement/systematically_label_spots.gif) + ## Tags ### Locate tags diff --git a/doc/spotsmanagement/systematically_label_spots.gif b/doc/spotsmanagement/systematically_label_spots.gif new file mode 100644 index 00000000..bcefad5b Binary files /dev/null and b/doc/spotsmanagement/systematically_label_spots.gif differ diff --git a/src/main/java/org/mastodon/mamut/tomancak/label_systematically/LabelSpotsSystematicallyDialog.java b/src/main/java/org/mastodon/mamut/tomancak/label_systematically/LabelSpotsSystematicallyDialog.java index ddd6eedd..37e4d640 100644 --- a/src/main/java/org/mastodon/mamut/tomancak/label_systematically/LabelSpotsSystematicallyDialog.java +++ b/src/main/java/org/mastodon/mamut/tomancak/label_systematically/LabelSpotsSystematicallyDialog.java @@ -69,7 +69,7 @@ public class LabelSpotsSystematicallyDialog extends JDialog private final JButton actionButton; private LabelSpotsSystematicallyDialog( ProjectModel appModel ) { - super(( Frame ) null, "Sort Lineage Tree", false); + super( ( Frame ) null, "Label spots systematically", false ); setResizable( false ); this.appModel = appModel; this.centerLandmark = new SelectSpotsComponent( appModel ); @@ -121,6 +121,11 @@ private void renameButtonClicked() try { Collection center = centerLandmark.getSelectedSpots(); + if ( center.isEmpty() ) + { + dispose(); + return; + } Collection selected = selectSpots.getSelectedSpots(); LabelSpotsSystematically.setLabelsBasedOnExternIntern( graph, center, selected, renameUnnamed, renameLabelsEndingWith1Or2 ); model.setUndoPoint(); diff --git a/src/main/java/org/mastodon/mamut/tomancak/sort_tree/ExternInternOrder.java b/src/main/java/org/mastodon/mamut/tomancak/sort_tree/ExternInternOrder.java index 3944c5ba..3649e9eb 100644 --- a/src/main/java/org/mastodon/mamut/tomancak/sort_tree/ExternInternOrder.java +++ b/src/main/java/org/mastodon/mamut/tomancak/sort_tree/ExternInternOrder.java @@ -30,12 +30,13 @@ import org.mastodon.mamut.model.ModelGraph; import org.mastodon.mamut.model.Spot; -import org.mastodon.mamut.tomancak.sort_tree.SortTreeUtils; import java.util.Collection; import java.util.List; import java.util.function.Predicate; +import net.imglib2.util.LinAlgHelpers; + /** * Returns true, if and only if, the first child of the given spot is further * away from the "center landmark" than the second child. @@ -59,9 +60,9 @@ public boolean test( Spot spot ) { if(spot.outgoingEdges().size() != 2) return true; - double[] devisionDirection = SortTreeUtils.directionOfCellDevision( graph, spot ); + double[] divisionDirection = SortTreeUtils.directionOfCellDivision( graph, spot ); double[] centerPosition = centerPositions.get( spot.getTimepoint() ); double[] centerDirection = SortTreeUtils.subtract( spot.positionAsDoubleArray(), centerPosition ); - return SortTreeUtils.scalarProduct( devisionDirection, centerDirection ) < 0; + return LinAlgHelpers.dot( divisionDirection, centerDirection ) < 0; } } diff --git a/src/main/java/org/mastodon/mamut/tomancak/sort_tree/LeftRightOrder.java b/src/main/java/org/mastodon/mamut/tomancak/sort_tree/LeftRightOrder.java index e9e3b545..ff744575 100644 --- a/src/main/java/org/mastodon/mamut/tomancak/sort_tree/LeftRightOrder.java +++ b/src/main/java/org/mastodon/mamut/tomancak/sort_tree/LeftRightOrder.java @@ -35,6 +35,8 @@ import java.util.List; import java.util.function.Predicate; +import net.imglib2.util.LinAlgHelpers; + public class LeftRightOrder implements Predicate { @@ -56,8 +58,8 @@ public boolean test( Spot spot ) { if (spot.outgoingEdges().size() != 2) return true; - double[] divisionDirection = SortTreeUtils.directionOfCellDevision( graph, spot ); + double[] divisionDirection = SortTreeUtils.directionOfCellDivision( graph, spot ); double[] sortingDirection = directions.get( spot.getTimepoint() ); - return SortTreeUtils.scalarProduct( sortingDirection, divisionDirection) >= 0; + return LinAlgHelpers.dot( sortingDirection, divisionDirection ) >= 0; } } diff --git a/src/main/java/org/mastodon/mamut/tomancak/sort_tree/SelectSpotsComponent.java b/src/main/java/org/mastodon/mamut/tomancak/sort_tree/SelectSpotsComponent.java index 69e0590f..932356bc 100644 --- a/src/main/java/org/mastodon/mamut/tomancak/sort_tree/SelectSpotsComponent.java +++ b/src/main/java/org/mastodon/mamut/tomancak/sort_tree/SelectSpotsComponent.java @@ -30,6 +30,7 @@ import org.mastodon.collection.RefSet; import org.mastodon.collection.ref.RefSetImp; +import org.mastodon.graph.algorithm.RootFinder; import org.mastodon.mamut.ProjectModel; import org.mastodon.mamut.model.Link; import org.mastodon.mamut.model.Model; @@ -156,10 +157,7 @@ private void addSubdividedLineageMenu( JPopupMenu popupMenu, List roots ) map.put( key, new ArrayList<>() ); for(Spot root : roots) { Character character = root.getLabel().charAt( 0 ); - if(map.containsKey( character )) - map.get( character ).add( root ); - else - other.add( root ); + map.getOrDefault( character, other ).add( root ); } map.forEach( (key, list) -> addLineageSubMenu( popupMenu, "\"" + key + "...\"", list ) ); addLineageSubMenu( popupMenu, "\"A-Z...\"", other ); @@ -184,15 +182,7 @@ private JMenuItem createLineageMenuItem( Spot root ) private List getRoots() { - ModelGraph graph = mastodonModel.getGraph(); - List roots = new ArrayList<>(); - for ( Spot spot : graph.vertices() ) - if ( spot.incomingEdges().size() == 0 ) - { - Spot ref = graph.vertexRef().refTo( spot ); - roots.add( ref ); - } - return roots; + return new ArrayList<>( RootFinder.getRoots( mastodonModel.getGraph() ) ); } private JMenu createTagSetMenu( TagSetStructure.TagSet tagSet ) diff --git a/src/main/java/org/mastodon/mamut/tomancak/sort_tree/SortTreeUtils.java b/src/main/java/org/mastodon/mamut/tomancak/sort_tree/SortTreeUtils.java index e761a76d..ba91e74c 100644 --- a/src/main/java/org/mastodon/mamut/tomancak/sort_tree/SortTreeUtils.java +++ b/src/main/java/org/mastodon/mamut/tomancak/sort_tree/SortTreeUtils.java @@ -50,7 +50,7 @@ public class SortTreeUtils /** * If {@code spot} is a {@link Spot} that divides at timepoint * {@code t = spot.getTimepoint}. Then the cell division direction - * returned by {@link #directionOfCellDevision} is sampled not at + * returned by {@link #directionOfCellDivision} is sampled not at * timepoint {@code t} but at a timepoint {@code s}. With * {@code s = t + DIVISION_DIRECTION_TIME_OFFSET}. */ @@ -62,7 +62,7 @@ public class SortTreeUtils * three time points. And returns the vector from the first daughter cell * to the second daughter cell. */ - public static double[] directionOfCellDevision( ModelGraph graph, Spot spot ) + public static double[] directionOfCellDivision( ModelGraph graph, Spot spot ) { if(spot.outgoingEdges().size() != 2) return new double[]{ 0, 0, 0 }; @@ -110,15 +110,6 @@ private static double[] averageStartingPosition( ModelGraph graph, int count, Sp } } - public static double scalarProduct( double[] a, double[] b ) - { - assert a.length == b.length; - double sum = 0; - for ( int i = 0; i < a.length; i++ ) - sum += a[ i ] * b[ i ]; - return sum; - } - static List subtract( List a, List b ) { final int n = a.size(); @@ -152,8 +143,8 @@ public static void divide( double[] average, int size ) } /** - * Given a collection of {@link Spot spots}, this the average position of - * the spots for each time point. The position is interpolated (or + * Given a collection of {@link Spot spots}, this method computes the average position of + * the spots at each time point. The position is interpolated (or * extrapolated), if there is a time point with no given cell. */ public static List calculateAndInterpolateAveragePosition( int numTimePoint, Collection spots ) @@ -292,7 +283,7 @@ public static int getNumberOfTimePoints( ModelGraph graph ) */ public static double angle( double[] directionA, double[] directionB ) { - double cos = scalarProduct( directionA, directionB ) / LinAlgHelpers.length( directionA ) / LinAlgHelpers.length( directionB ); + double cos = LinAlgHelpers.dot( directionA, directionB ) / LinAlgHelpers.length( directionA ) / LinAlgHelpers.length( directionB ); return Math.acos( cos ); } diff --git a/src/main/java/org/mastodon/mamut/tomancak/trackmatching/SpatialTrackMatchingAlgorithm.java b/src/main/java/org/mastodon/mamut/tomancak/trackmatching/SpatialTrackMatchingAlgorithm.java index 48fdfa35..4d34056f 100644 --- a/src/main/java/org/mastodon/mamut/tomancak/trackmatching/SpatialTrackMatchingAlgorithm.java +++ b/src/main/java/org/mastodon/mamut/tomancak/trackmatching/SpatialTrackMatchingAlgorithm.java @@ -48,7 +48,7 @@ * By doing so it figures out which spots need to be flipped in order * to match the TrackSchemes of both lineages. * - * @see SortTreeUtils#directionOfCellDevision(ModelGraph, Spot) + * @see SortTreeUtils#directionOfCellDivision(ModelGraph, Spot) */ public class SpatialTrackMatchingAlgorithm { @@ -164,8 +164,8 @@ private void matchTree( Spot rootA, Spot rootB ) dividingB.outgoingEdges().size() == 2; if ( !bothDivide ) return; - double[] directionA = SortTreeUtils.directionOfCellDevision( graphA, dividingA ); - double[] directionB = SortTreeUtils.directionOfCellDevision( graphB, dividingB ); + double[] directionA = SortTreeUtils.directionOfCellDivision( graphA, dividingA ); + double[] directionB = SortTreeUtils.directionOfCellDivision( graphB, dividingB ); AffineTransform3D transformAB = noOffsetTransform( spatialRegistration.getTransformationAtoB( dividingA.getTimepoint() + TIME_OFFSET, dividingB.getTimepoint() + TIME_OFFSET ) ); transformAB.apply( directionA, directionA ); diff --git a/src/test/java/org/mastodon/mamut/tomancak/sort_tree/SortTreeUtilsTest.java b/src/test/java/org/mastodon/mamut/tomancak/sort_tree/SortTreeUtilsTest.java index 86e8a142..71a7746e 100644 --- a/src/test/java/org/mastodon/mamut/tomancak/sort_tree/SortTreeUtilsTest.java +++ b/src/test/java/org/mastodon/mamut/tomancak/sort_tree/SortTreeUtilsTest.java @@ -43,7 +43,8 @@ public class SortTreeUtilsTest { @Test - public void testDirectionOfCellDevision() { + public void testDirectionOfCellDivision() + { // setup ModelGraph graph = new ModelGraph(); Spot spot = graph.addVertex().init( 0, array(2, 2, 2), 0.5 ); @@ -52,13 +53,14 @@ public void testDirectionOfCellDevision() { graph.addEdge( spot, a ).init(); graph.addEdge( spot, b ).init(); // process - double[] direction = SortTreeUtils.directionOfCellDevision( graph, spot ); + double[] direction = SortTreeUtils.directionOfCellDivision( graph, spot ); // test assertArrayEquals(array(2, 0, 0), direction, 0.0); } @Test - public void testDirectionOfCellDevision2() { + public void testDirectionOfCellDivision2() + { // setup ModelGraph graph = new ModelGraph(); Spot spot = graph.addVertex().init( 0, array(2, 2, 2), 0.5 ); @@ -79,7 +81,7 @@ public void testDirectionOfCellDevision2() { graph.addEdge( b2, b3_1 ).init(); graph.addEdge( b2, b3_2 ).init(); // process - double[] direction = SortTreeUtils.directionOfCellDevision( graph, spot ); + double[] direction = SortTreeUtils.directionOfCellDivision( graph, spot ); // test assertArrayEquals(array(0.5, 0, 0), direction, 0.0);