Skip to content

Commit 7b7d154

Browse files
authored
Merge pull request #10 from mastodon-sc/lineage-registration
Add lineage registration plugin, that allow comparing the lineages of two stereotypically developing embryos.
2 parents c6d13d1 + 38d6faa commit 7b7d154

35 files changed

Lines changed: 3277 additions & 5 deletions

mastodon-coding-style.xml

Lines changed: 399 additions & 0 deletions
Large diffs are not rendered by default.

pom.xml

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@
4141
<groupId>org.scijava</groupId>
4242
<artifactId>ui-behaviour</artifactId>
4343
</dependency>
44+
<dependency>
45+
<groupId>mpicbg</groupId>
46+
<artifactId>mpicbg</artifactId>
47+
</dependency>
4448

4549
<!-- Test dependencies -->
4650
<dependency>
@@ -80,7 +84,7 @@
8084
<license.organizationName>Mastodon authors</license.organizationName>
8185
<license.copyrightOwners>Tobias Pietzsch</license.copyrightOwners>
8286

83-
<mastodon.version>1.0.0-beta-26</mastodon.version>
87+
<mastodon.version>1.0.0-beta-27-SNAPSHOT</mastodon.version>
8488

8589
<!-- NB: Deploy releases to the SciJava Maven repository. -->
8690
<releaseProfiles>sign,deploy-to-scijava</releaseProfiles>
@@ -111,4 +115,19 @@
111115
<url>https://maven.scijava.org/content/groups/public</url>
112116
</repository>
113117
</repositories>
118+
119+
<build>
120+
<pluginManagement>
121+
<plugins>
122+
<plugin>
123+
<groupId>net.revelc.code.formatter</groupId>
124+
<artifactId>formatter-maven-plugin</artifactId>
125+
<version>${formatter-maven-plugin.version}</version>
126+
<configuration>
127+
<configFile>mastodon-coding-style.xml</configFile>
128+
</configuration>
129+
</plugin>
130+
</plugins>
131+
</pluginManagement>
132+
</build>
114133
</project>

src/main/java/org/mastodon/mamut/tomancak/TomancakPlugins.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public class TomancakPlugins extends AbstractContextual implements MamutPlugin
7777
private static final String COMPACT_LINEAGE_VIEW = "[tomancak] show compact lineage";
7878
private static final String SORT_TREE = "[tomancak] sort lineage tree";
7979
private static final String SORT_TREE_EXTERN_INTERN = "[tomancak] sort lineage tree extern intern";
80+
8081
private static final String LABEL_SPOTS_SYSTEMATICALLY = "[tomancak] label spots systematically";
8182
private static final String REMOVE_SOLISTS_SPOTS = "[tomancak] remove solists spots";
8283
private static final String EXPORTS_LINEAGE_LENGTHS = "[tomancak] export lineage lengths";
@@ -93,6 +94,7 @@ public class TomancakPlugins extends AbstractContextual implements MamutPlugin
9394
private static final String[] COMPACT_LINEAGE_VIEW_KEYS = { "not mapped" };
9495
private static final String[] SORT_TREE_KEYS = { "ctrl S" };
9596
private static final String[] SORT_TREE_EXTERN_INTERN_KEYS = { "not mapped" };
97+
9698
private static final String[] LABEL_SPOTS_SYSTEMATICALLY_KEYS = { "not mapped" };
9799
private static final String[] REMOVE_SOLISTS_SPOTS_KEYS = { "not mapped" };
98100
private static final String[] EXPORTS_LINEAGE_LENGTHS_KEYS = { "not mapped" };
@@ -112,8 +114,8 @@ public class TomancakPlugins extends AbstractContextual implements MamutPlugin
112114
menuTexts.put( CHANGE_BRANCH_LABELS, "Change Branch's Labels");
113115
menuTexts.put( COMPACT_LINEAGE_VIEW, "Show Compact Lineage" );
114116
menuTexts.put( SORT_TREE, "Sort Lineage Tree" );
115-
menuTexts.put( SORT_TREE_EXTERN_INTERN, "Sort Lineage Tree, Extern-Intern");
116-
menuTexts.put( LABEL_SPOTS_SYSTEMATICALLY, "Systematically Label Spots, Extern-Intern" );
117+
menuTexts.put( SORT_TREE_EXTERN_INTERN, "Sort Lineage Tree (Extern-Intern)" );
118+
menuTexts.put( LABEL_SPOTS_SYSTEMATICALLY, "Systematically Label Spots (Extern-Intern)" );
117119
menuTexts.put( REMOVE_SOLISTS_SPOTS, "Remove Spots Solists" );
118120
menuTexts.put( EXPORTS_LINEAGE_LENGTHS, "Export Lineage Lengths" );
119121
menuTexts.put( EXPORTS_SPOTS_COUNTS, "Export Spots Counts" );
@@ -228,7 +230,7 @@ public List< ViewMenuBuilder.MenuItem > getMenuItems()
228230
item( FLIP_DESCENDANTS ),
229231
item( SORT_TREE ),
230232
item( SORT_TREE_EXTERN_INTERN ),
231-
item( LABEL_SPOTS_SYSTEMATICALLY )),
233+
item( LABEL_SPOTS_SYSTEMATICALLY ) ),
232234
menu( "Exports",
233235
item( EXPORTS_LINEAGE_LENGTHS ),
234236
item( EXPORTS_SPOTS_COUNTS ),
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*-
2+
* #%L
3+
* A Mastodon plugin data allows to show the embryo in Blender.
4+
* %%
5+
* Copyright (C) 2022 - 2023 Matthias Arzt
6+
* %%
7+
* Redistribution and use in source and binary forms, with or without
8+
* modification, are permitted provided that the following conditions are met:
9+
*
10+
* 1. Redistributions of source code must retain the above copyright notice,
11+
* this list of conditions and the following disclaimer.
12+
* 2. Redistributions in binary form must reproduce the above copyright notice,
13+
* this list of conditions and the following disclaimer in the documentation
14+
* and/or other materials provided with the distribution.
15+
*
16+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
20+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26+
* POSSIBILITY OF SUCH DAMAGE.
27+
* #L%
28+
*/
29+
package org.mastodon.mamut.tomancak.lineage_registration;
30+
31+
import net.imglib2.util.Pair;
32+
import net.imglib2.util.ValuePair;
33+
34+
import org.mastodon.collection.RefList;
35+
import org.mastodon.collection.RefSet;
36+
import org.mastodon.collection.ref.RefArrayList;
37+
import org.mastodon.collection.ref.RefSetImp;
38+
import org.mastodon.mamut.model.Link;
39+
import org.mastodon.mamut.model.ModelGraph;
40+
import org.mastodon.mamut.model.Spot;
41+
42+
public class BranchGraphUtils
43+
{
44+
private BranchGraphUtils()
45+
{
46+
// prevent instantiation
47+
}
48+
49+
public static Spot getBranchStart( Spot spot, Spot ref )
50+
{
51+
Spot s = spot;
52+
while ( s.incomingEdges().size() == 1 )
53+
{
54+
Link edge = s.incomingEdges().iterator().next();
55+
s = edge.getSource( ref );
56+
if ( s.outgoingEdges().size() != 1 )
57+
return edge.getTarget( ref );
58+
}
59+
return s;
60+
}
61+
62+
public static Spot getBranchEnd( Spot spot, Spot ref )
63+
{
64+
Spot s = spot;
65+
while ( s.outgoingEdges().size() == 1 )
66+
{
67+
Link edge = s.outgoingEdges().iterator().next();
68+
s = edge.getTarget( ref );
69+
if ( s.incomingEdges().size() != 1 )
70+
return edge.getSource( ref );
71+
}
72+
return s;
73+
}
74+
75+
static Spot findVertexForTimePoint( Spot branchStart, int timePoint, Spot ref )
76+
{
77+
Spot spot = branchStart;
78+
if ( spot.getTimepoint() >= timePoint )
79+
return spot;
80+
while ( spot.outgoingEdges().size() == 1 )
81+
{
82+
spot = spot.outgoingEdges().iterator().next().getTarget( ref );
83+
if ( spot.getTimepoint() >= timePoint )
84+
return spot;
85+
}
86+
return spot;
87+
}
88+
89+
static Pair< RefList< Spot >, RefList< Link > > getBranchSpotsAndLinks( ModelGraph graph, Spot branchStart )
90+
{
91+
RefList< Link > links = new RefArrayList<>( graph.edges().getRefPool() );
92+
RefList< Spot > spots = new RefArrayList<>( graph.vertices().getRefPool() );
93+
spots.add( branchStart );
94+
Spot ref = graph.vertexRef();
95+
Spot spot = branchStart;
96+
while ( spot.outgoingEdges().size() == 1 )
97+
{
98+
Link link = spot.outgoingEdges().iterator().next();
99+
spot = link.getTarget( ref );
100+
if ( spot.incomingEdges().size() != 1 )
101+
break;
102+
links.add( link );
103+
spots.add( spot );
104+
}
105+
graph.releaseRef( ref );
106+
return new ValuePair<>( spots, links );
107+
}
108+
109+
static RefSet< Spot > getAllBranchStarts( ModelGraph graph )
110+
{
111+
Spot ref = graph.vertexRef();
112+
try
113+
{
114+
RefSet< Spot > set = new RefSetImp<>( graph.vertices().getRefPool() );
115+
for ( Spot spot : graph.vertices() )
116+
{
117+
if ( spot.incomingEdges().size() != 1 )
118+
set.add( spot );
119+
if ( spot.outgoingEdges().size() > 1 )
120+
for ( Link link : spot.outgoingEdges() )
121+
set.add( link.getTarget( ref ) );
122+
}
123+
return set;
124+
}
125+
finally
126+
{
127+
graph.releaseRef( ref );
128+
}
129+
}
130+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.mastodon.mamut.tomancak.lineage_registration;
2+
3+
/**
4+
* Similar to {@link AutoCloseable}, but without the checked exception.
5+
*/
6+
interface ClosableLock extends AutoCloseable
7+
{
8+
void close();
9+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package org.mastodon.mamut.tomancak.lineage_registration;
2+
3+
import java.awt.Component;
4+
import java.util.Arrays;
5+
import java.util.List;
6+
import java.util.function.Function;
7+
8+
import javax.swing.JOptionPane;
9+
10+
import net.imglib2.util.Cast;
11+
12+
public class ComboBoxDialog
13+
{
14+
15+
public static < T > T showComboBoxDialog( Component parent, String title, String message, List< T > options,
16+
Function< T, String > toString )
17+
{
18+
Choice[] choices = options.stream()
19+
.map( option -> new Choice( toString.apply( option ), option ) )
20+
.toArray( Choice[]::new );
21+
Choice choice = ( Choice ) JOptionPane.showInputDialog( parent, message, title,
22+
JOptionPane.QUESTION_MESSAGE, null, choices, choices[ 0 ] );
23+
return choice != null ? Cast.unchecked( choice.getValue() ) : null;
24+
}
25+
26+
private static class Choice
27+
{
28+
private final String name;
29+
30+
private final Object value;
31+
32+
public Choice( String name, Object value )
33+
{
34+
this.name = name;
35+
this.value = value;
36+
}
37+
38+
public Object getValue()
39+
{
40+
return value;
41+
}
42+
43+
@Override
44+
public String toString()
45+
{
46+
return name;
47+
}
48+
}
49+
50+
public static void main( String... args )
51+
{
52+
// NB: Small demo method
53+
List< String > options = Arrays.asList( "a", "b", "c" );
54+
String choice = showComboBoxDialog( null, "title", "message", options, a -> "choose: " + a );
55+
System.out.println( choice );
56+
}
57+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package org.mastodon.mamut.tomancak.lineage_registration;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
import mpicbg.models.IllDefinedDataPointsException;
7+
import mpicbg.models.NotEnoughDataPointsException;
8+
import mpicbg.models.Point;
9+
import mpicbg.models.PointMatch;
10+
import mpicbg.models.SimilarityModel3D;
11+
12+
import net.imglib2.realtransform.AffineTransform3D;
13+
14+
import org.mastodon.collection.RefRefMap;
15+
import org.mastodon.mamut.model.Spot;
16+
17+
/**
18+
* This class holds a function for estimating a 3d rigid transformation
19+
* between two sets of points.
20+
*/
21+
class EstimateTransformation
22+
{
23+
24+
private EstimateTransformation()
25+
{
26+
// prevent instantiation
27+
}
28+
29+
/**
30+
* <p>
31+
* @return a 3d rigid transform (that is composed of scaling, rotation and translation).
32+
* The returned transformation is the optimal transformation, that when applied on the "key" spots
33+
* of the given "pairs" map minimizes the distance to the "value" spots.
34+
* </p>
35+
* <p>
36+
* See "Closed-form solution of absolute orientation using unit quaternions",
37+
* Horn, B. K. P., Journal of the Optical Society of America A, Vol. 4, page 629, April 1987
38+
* </p>
39+
* @param pairs A map that serves as a list of pairs of spots. Each pair consists of a "key" spot
40+
* and a "value" spot. The algorithm only uses the coordinates of the spots, all other
41+
* properties are ignored.
42+
* @see SimilarityModel3D
43+
*/
44+
public static AffineTransform3D estimateScaleRotationAndTranslation( RefRefMap< Spot, Spot > pairs )
45+
{
46+
// NB: This method is not as memory efficient as it could be.
47+
// It creates multiple objects (Point, PointMatch, arrays) per item in "pairs".
48+
// Memory efficiency should be improved if performance problems arise.
49+
Spot refB = pairs.createValueRef();
50+
try
51+
{
52+
List< PointMatch > matches = new ArrayList<>( pairs.size() );
53+
for ( Spot spotA : pairs.keySet() )
54+
{
55+
Spot spotB = pairs.get( spotA );
56+
Point pointA = new Point( spotA.positionAsDoubleArray() );
57+
Point pointB = new Point( spotB.positionAsDoubleArray() );
58+
matches.add( new PointMatch( pointA, pointB, 1 ) );
59+
}
60+
return fitTransform( matches );
61+
}
62+
finally
63+
{
64+
pairs.releaseValueRef( refB );
65+
}
66+
}
67+
68+
private static AffineTransform3D fitTransform( List< PointMatch > matches )
69+
{
70+
try
71+
{
72+
SimilarityModel3D model = new SimilarityModel3D();
73+
model.fit( matches );
74+
AffineTransform3D transform = new AffineTransform3D();
75+
transform.set( model.getMatrix( null ) );
76+
return transform;
77+
}
78+
catch ( NotEnoughDataPointsException | IllDefinedDataPointsException e )
79+
{
80+
throw new RuntimeException( e );
81+
}
82+
}
83+
}

0 commit comments

Comments
 (0)