Skip to content

Commit f537640

Browse files
authored
Merge pull request #210 from fiji/auto-naming
Add an action that performs auto-naming of spots based on rules.
2 parents 401c767 + ed3699c commit f537640

10 files changed

Lines changed: 704 additions & 4 deletions

File tree

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*-
2+
* #%L
3+
* Fiji distribution of ImageJ for the life sciences.
4+
* %%
5+
* Copyright (C) 2010 - 2022 Fiji developers.
6+
* %%
7+
* This program is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as
9+
* published by the Free Software Foundation, either version 3 of the
10+
* License, or (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public
18+
* License along with this program. If not, see
19+
* <http://www.gnu.org/licenses/gpl-3.0.html>.
20+
* #L%
21+
*/
22+
package fiji.plugin.trackmate.action.autonaming;
23+
24+
import javax.swing.ImageIcon;
25+
26+
import org.scijava.plugin.Plugin;
27+
28+
import fiji.plugin.trackmate.SelectionModel;
29+
import fiji.plugin.trackmate.TrackMate;
30+
import fiji.plugin.trackmate.action.AbstractTMAction;
31+
import fiji.plugin.trackmate.action.TrackMateAction;
32+
import fiji.plugin.trackmate.action.TrackMateActionFactory;
33+
import fiji.plugin.trackmate.gui.Icons;
34+
import fiji.plugin.trackmate.gui.displaysettings.DisplaySettings;
35+
36+
public class AutoNamingAction extends AbstractTMAction
37+
{
38+
39+
public static final String INFO_TEXT = "<html>"
40+
+ "Rename individual spots based on auto-naming rules. "
41+
+ "All spot names are changed. There is no undo.</html>";
42+
43+
@Override
44+
public void execute( final TrackMate trackmate, final SelectionModel selectionModel, final DisplaySettings displaySettings, final java.awt.Frame parent )
45+
{
46+
final AutoNamingController controller = new AutoNamingController( trackmate, logger );
47+
controller.show();
48+
}
49+
50+
@Plugin( type = TrackMateActionFactory.class )
51+
public static class Factory implements TrackMateActionFactory
52+
{
53+
54+
public static final String NAME = "Spot auto-naming";
55+
56+
public static final String KEY = "AUTO_NAMING";
57+
58+
@Override
59+
public String getInfoText()
60+
{
61+
return INFO_TEXT;
62+
}
63+
64+
@Override
65+
public String getKey()
66+
{
67+
return KEY;
68+
}
69+
70+
@Override
71+
public TrackMateAction create()
72+
{
73+
return new AutoNamingAction();
74+
}
75+
76+
@Override
77+
public ImageIcon getIcon()
78+
{
79+
return Icons.PENCIL_ICON;
80+
}
81+
82+
@Override
83+
public String getName()
84+
{
85+
return NAME;
86+
}
87+
}
88+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*-
2+
* #%L
3+
* Fiji distribution of ImageJ for the life sciences.
4+
* %%
5+
* Copyright (C) 2010 - 2022 Fiji developers.
6+
* %%
7+
* This program is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as
9+
* published by the Free Software Foundation, either version 3 of the
10+
* License, or (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public
18+
* License along with this program. If not, see
19+
* <http://www.gnu.org/licenses/gpl-3.0.html>.
20+
* #L%
21+
*/
22+
package fiji.plugin.trackmate.action.autonaming;
23+
24+
import java.util.ArrayList;
25+
import java.util.Collection;
26+
27+
import javax.swing.JFrame;
28+
import javax.swing.JLabel;
29+
30+
import fiji.plugin.trackmate.Logger;
31+
import fiji.plugin.trackmate.TrackMate;
32+
import fiji.plugin.trackmate.gui.GuiUtils;
33+
import fiji.plugin.trackmate.gui.Icons;
34+
import fiji.plugin.trackmate.util.EverythingDisablerAndReenabler;
35+
36+
public class AutoNamingController
37+
{
38+
39+
private final TrackMate trackmate;
40+
41+
private final AutoNamingPanel gui;
42+
43+
private final Logger logger;
44+
45+
public AutoNamingController( final TrackMate trackmate, final Logger logger )
46+
{
47+
this.trackmate = trackmate;
48+
this.logger = logger;
49+
50+
final Collection< AutoNamingRule > namingRules = new ArrayList<>( 3 );
51+
namingRules.add( new CopyTrackNameNamingRule() );
52+
namingRules.add( new DefaultAutoNamingRule( ".", "", false ) );
53+
namingRules.add( new DefaultAutoNamingRule( ".", "", true ) );
54+
55+
this.gui = new AutoNamingPanel( namingRules );
56+
57+
gui.btnRun.addActionListener( e -> run( ( ( AutoNamingRule ) gui.cmbboxRule.getSelectedItem() ) ) );
58+
}
59+
60+
private void run( final AutoNamingRule autoNaming )
61+
{
62+
final EverythingDisablerAndReenabler disabler = new EverythingDisablerAndReenabler( gui, new Class[] { JLabel.class } );
63+
disabler.disable();
64+
new Thread( "TrackMateAutoNamingThread" )
65+
{
66+
@Override
67+
public void run()
68+
{
69+
try
70+
{
71+
logger.log( "Applying nameing rule: " + autoNaming.toString() + ".\n" );
72+
logger.setStatus( "Spot auto-naming" );
73+
AutoNamingPerformer.autoNameSpots( trackmate.getModel(), autoNaming );
74+
trackmate.getModel().notifyFeaturesComputed();
75+
logger.log( "Spot auto-naming done.\n" );
76+
}
77+
finally
78+
{
79+
disabler.reenable();
80+
}
81+
}
82+
}.start();
83+
}
84+
85+
public void show()
86+
{
87+
if ( gui.getParent() != null && gui.getParent().isVisible() )
88+
return;
89+
90+
final JFrame frame = new JFrame( "Spot auto-naming" );
91+
frame.setIconImage( Icons.TRACK_SCHEME_ICON.getImage() );
92+
frame.setSize( 500, 400 );
93+
frame.getContentPane().add( gui );
94+
GuiUtils.positionWindow( frame, trackmate.getSettings().imp.getCanvas() );
95+
frame.setVisible( true );
96+
}
97+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package fiji.plugin.trackmate.action.autonaming;
2+
3+
import java.awt.GridBagConstraints;
4+
import java.awt.GridBagLayout;
5+
import java.awt.Insets;
6+
import java.util.Collection;
7+
import java.util.Vector;
8+
9+
import javax.swing.BorderFactory;
10+
import javax.swing.JButton;
11+
import javax.swing.JComboBox;
12+
import javax.swing.JLabel;
13+
import javax.swing.JPanel;
14+
import javax.swing.SwingConstants;
15+
16+
import fiji.plugin.trackmate.gui.Fonts;
17+
18+
public class AutoNamingPanel extends JPanel
19+
{
20+
21+
private static final long serialVersionUID = 1L;
22+
23+
final JComboBox< AutoNamingRule > cmbboxRule;
24+
25+
final JButton btnRun;
26+
27+
public AutoNamingPanel( final Collection< AutoNamingRule > namingRules )
28+
{
29+
setBorder( BorderFactory.createEmptyBorder( 5, 5, 5, 5 ) );
30+
final GridBagLayout gridBagLayout = new GridBagLayout();
31+
gridBagLayout.columnWidths = new int[] { 0, 0, 0 };
32+
gridBagLayout.rowHeights = new int[] { 0, 0, 0, 0, 0, 0 };
33+
gridBagLayout.columnWeights = new double[] { 1.0, 1.0, Double.MIN_VALUE };
34+
gridBagLayout.rowWeights = new double[] { 0.0, 1.0, 0.0, 1.0, 0.0, Double.MIN_VALUE };
35+
setLayout( gridBagLayout );
36+
37+
final JLabel lblTitle = new JLabel( "Auto naming spots" );
38+
lblTitle.setFont( Fonts.BIG_FONT );
39+
lblTitle.setHorizontalAlignment( SwingConstants.CENTER );
40+
final GridBagConstraints gbcLblTitle = new GridBagConstraints();
41+
gbcLblTitle.gridwidth = 2;
42+
gbcLblTitle.insets = new Insets( 0, 0, 5, 0 );
43+
gbcLblTitle.fill = GridBagConstraints.HORIZONTAL;
44+
gbcLblTitle.gridx = 0;
45+
gbcLblTitle.gridy = 0;
46+
add( lblTitle, gbcLblTitle );
47+
48+
final JLabel lblDoc = new JLabel( AutoNamingAction.INFO_TEXT );
49+
lblDoc.setFont( Fonts.SMALL_FONT );
50+
final GridBagConstraints gbcLblDoc = new GridBagConstraints();
51+
gbcLblDoc.gridwidth = 2;
52+
gbcLblDoc.insets = new Insets( 0, 0, 5, 0 );
53+
gbcLblDoc.fill = GridBagConstraints.BOTH;
54+
gbcLblDoc.gridx = 0;
55+
gbcLblDoc.gridy = 1;
56+
add( lblDoc, gbcLblDoc );
57+
58+
final JLabel lblRule = new JLabel( "Naming rule" );
59+
lblRule.setFont( Fonts.SMALL_FONT );
60+
final GridBagConstraints gbcLblRule = new GridBagConstraints();
61+
gbcLblRule.anchor = GridBagConstraints.EAST;
62+
gbcLblRule.insets = new Insets( 0, 0, 5, 5 );
63+
gbcLblRule.gridx = 0;
64+
gbcLblRule.gridy = 2;
65+
add( lblRule, gbcLblRule );
66+
67+
cmbboxRule = new JComboBox<>( new Vector<>( namingRules ) );
68+
cmbboxRule.setFont( Fonts.SMALL_FONT );
69+
70+
final GridBagConstraints gbcCmbboxRule = new GridBagConstraints();
71+
gbcCmbboxRule.insets = new Insets( 0, 0, 5, 0 );
72+
gbcCmbboxRule.fill = GridBagConstraints.HORIZONTAL;
73+
gbcCmbboxRule.gridx = 1;
74+
gbcCmbboxRule.gridy = 2;
75+
add( cmbboxRule, gbcCmbboxRule );
76+
77+
final JLabel lblRuleInfo = new JLabel();
78+
lblRuleInfo.setFont( Fonts.SMALL_FONT );
79+
final GridBagConstraints gbcLblRuleInfo = new GridBagConstraints();
80+
gbcLblRuleInfo.fill = GridBagConstraints.BOTH;
81+
gbcLblRuleInfo.insets = new Insets( 0, 0, 5, 0 );
82+
gbcLblRuleInfo.gridwidth = 2;
83+
gbcLblRuleInfo.gridx = 0;
84+
gbcLblRuleInfo.gridy = 3;
85+
add( lblRuleInfo, gbcLblRuleInfo );
86+
87+
btnRun = new JButton( "Run" );
88+
final GridBagConstraints gbcBtnRun = new GridBagConstraints();
89+
gbcBtnRun.anchor = GridBagConstraints.EAST;
90+
gbcBtnRun.gridx = 1;
91+
gbcBtnRun.gridy = 4;
92+
add( btnRun, gbcBtnRun );
93+
94+
/*
95+
* Listeners.
96+
*/
97+
98+
cmbboxRule.addActionListener( e -> lblRuleInfo.setText( ( ( AutoNamingRule ) cmbboxRule.getSelectedItem() ).getInfoText() ) );
99+
cmbboxRule.setSelectedIndex( 0 );
100+
}
101+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package fiji.plugin.trackmate.action.autonaming;
2+
3+
import java.util.ArrayList;
4+
import java.util.Collection;
5+
import java.util.List;
6+
import java.util.Set;
7+
import java.util.stream.Collectors;
8+
9+
import org.jgrapht.Graphs;
10+
import org.jgrapht.graph.DefaultEdge;
11+
import org.jgrapht.graph.SimpleDirectedGraph;
12+
import org.jgrapht.traverse.DepthFirstIterator;
13+
14+
import fiji.plugin.trackmate.Model;
15+
import fiji.plugin.trackmate.Spot;
16+
import fiji.plugin.trackmate.TrackModel;
17+
import fiji.plugin.trackmate.graph.ConvexBranchesDecomposition;
18+
import fiji.plugin.trackmate.graph.ConvexBranchesDecomposition.TrackBranchDecomposition;
19+
import fiji.plugin.trackmate.graph.TimeDirectedNeighborIndex;
20+
21+
public class AutoNamingPerformer
22+
{
23+
24+
public static void autoNameSpots( final Model model, final AutoNamingRule rule )
25+
{
26+
final TimeDirectedNeighborIndex neighborIndex = model.getTrackModel().getDirectedNeighborIndex();
27+
for ( final Integer trackID : model.getTrackModel().unsortedTrackIDs( true ) )
28+
{
29+
final TrackBranchDecomposition branchDecomposition = ConvexBranchesDecomposition.processTrack( trackID, model.getTrackModel(), neighborIndex, true, false );
30+
final SimpleDirectedGraph< List< Spot >, DefaultEdge > branchGraph = ConvexBranchesDecomposition.buildBranchGraph( branchDecomposition );
31+
processTrack( rule, model.getTrackModel(), branchGraph );
32+
}
33+
}
34+
35+
private static void processTrack(
36+
final AutoNamingRule rule,
37+
final TrackModel model,
38+
final SimpleDirectedGraph< List< Spot >, DefaultEdge > graph )
39+
{
40+
// Find the roots. Might be several.
41+
final List< List< Spot > > roots = graph.vertexSet().stream()
42+
.filter( key -> graph.incomingEdgesOf( key ).size() == 0 )
43+
.collect( Collectors.toList() );
44+
45+
for ( final List< Spot > root : roots )
46+
{
47+
// Name the spots in the root branch.
48+
final Spot first = root.get( 0 );
49+
rule.nameRoot( first, model );
50+
51+
// Other spots in the root branch.
52+
Spot predecessor = first;
53+
for ( int i = 1; i < root.size(); i++ )
54+
{
55+
final Spot current = root.get( i );
56+
rule.nameSpot( current, predecessor );
57+
predecessor = current;
58+
}
59+
60+
// Iterate through branch, settings the name of children.
61+
final DepthFirstIterator< List< Spot >, DefaultEdge > it = new DepthFirstIterator<>( graph, root );
62+
while ( it.hasNext() )
63+
{
64+
final List< Spot > currentBranch = it.next();
65+
66+
// Collect children branches.
67+
final List< List< Spot > > childrenBranches = new ArrayList<>();
68+
final Set< DefaultEdge > edges = graph.outgoingEdgesOf( currentBranch );
69+
for ( final DefaultEdge edge : edges )
70+
{
71+
final List< Spot > cb = Graphs.getOppositeVertex( graph, edge, currentBranch );
72+
childrenBranches.add( cb );
73+
}
74+
75+
// Build siblings spots (first one of the children branch.
76+
final Collection< Spot > siblings = new ArrayList<>( childrenBranches.size() );
77+
for ( final List< Spot > cb : childrenBranches )
78+
siblings.add( cb.get( 0 ) );
79+
80+
// Collect mother spot (last one of current branch).
81+
final Spot mother = currentBranch.get( currentBranch.size() - 1 );
82+
83+
// Name the branch first spots.
84+
rule.nameBranches( mother, siblings );
85+
86+
// Name the spots inside each branch.
87+
for ( final List< Spot > cb : childrenBranches )
88+
{
89+
Spot parent = cb.get( 0 );
90+
for ( int i = 1; i < cb.size(); i++ )
91+
{
92+
final Spot current = cb.get( i );
93+
rule.nameSpot( current, parent );
94+
parent = current;
95+
}
96+
}
97+
}
98+
}
99+
}
100+
}

0 commit comments

Comments
 (0)