-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add plugin to create tag set for the highlighting of cell divisions #54
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
/*- | ||
* #%L | ||
* mastodon-tomancak | ||
* %% | ||
* Copyright (C) 2018 - 2024 Tobias Pietzsch | ||
* %% | ||
* Redistribution and use in source and binary forms, with or without | ||
* modification, are permitted provided that the following conditions are met: | ||
* | ||
* 1. Redistributions of source code must retain the above copyright notice, | ||
* this list of conditions and the following disclaimer. | ||
* 2. Redistributions in binary form must reproduce the above copyright notice, | ||
* this list of conditions and the following disclaimer in the documentation | ||
* and/or other materials provided with the distribution. | ||
* | ||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE | ||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
* POSSIBILITY OF SUCH DAMAGE. | ||
* #L% | ||
*/ | ||
package org.mastodon.mamut.tomancak.divisiontagset; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.Iterator; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.IntStream; | ||
|
||
import org.apache.commons.lang3.tuple.Pair; | ||
import org.mastodon.mamut.ProjectModel; | ||
import org.mastodon.mamut.model.Model; | ||
import org.mastodon.mamut.model.Spot; | ||
import org.mastodon.mamut.model.branch.BranchSpot; | ||
import org.mastodon.mamut.model.branch.ModelBranchGraph; | ||
import org.mastodon.model.tag.TagSetStructure; | ||
import org.mastodon.util.ColorUtils; | ||
import org.mastodon.util.TagSetUtils; | ||
import org.scijava.Cancelable; | ||
import org.scijava.command.Command; | ||
import org.scijava.plugin.Parameter; | ||
import org.scijava.plugin.Plugin; | ||
import org.scijava.util.ColorRGB; | ||
|
||
/** | ||
* {@link Command A SciJava command} that is called by the {@link CellDivisionsTagSetPlugin}. | ||
* <p> | ||
* The command creates a tag set that highlights cell divisions. The user interface allows to | ||
* specify the number of spots to highlight before and after a division, and the highlight and | ||
* background colors. | ||
*/ | ||
@Plugin( type = Command.class, label = "Add a Tag Set to Highlight Cell Divisions", visible = false ) | ||
public class CellDivisionsTagSetCommand implements Command, Cancelable | ||
{ | ||
@Parameter | ||
private ProjectModel projectModel; | ||
|
||
@Parameter( label = "Tag set name" ) | ||
private String tagSetName = "cell divisions"; | ||
|
||
@Parameter( label = "Default color" ) | ||
private ColorRGB defaultColor = new ColorRGB( "dark gray" ); | ||
|
||
@Parameter( label = "Highlight color" ) | ||
private ColorRGB highlightColor = new ColorRGB( "pink" ); | ||
|
||
@Parameter( label = "Timepoints to highlight before and after cell division" ) | ||
private int n = 3; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this maybe could have got a little longer name... down in the code I realized I had to search what 'n' is... but the code is short, so easy :-) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I simply have no idea how to name the variable properly. |
||
|
||
private List< TagSetStructure.Tag > tagsBefore; | ||
|
||
private List< TagSetStructure.Tag > tagsAfter; | ||
|
||
private TagSetStructure.Tag backgroundTag; | ||
|
||
private Model model; | ||
|
||
@Override | ||
public void run() | ||
{ | ||
projectModel.getBranchGraphSync().sync(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When I run the the plugin on this dataset: https://github.com/mastodon-sc/mastodon-example-data/tree/master/tgmm-mini with these settings:
I am not sure, if this related to this PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it's related to this PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The exception does not appear if I execute the code inside a full Fiji installation. I guess this exception is specific to some version of some scijava artifact. |
||
model = projectModel.getModel(); | ||
createTagSet(); | ||
applyTags(); | ||
} | ||
|
||
private void createTagSet() | ||
{ | ||
int defaultColor = this.defaultColor.getARGB(); | ||
maarzt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
int highlightColor = this.highlightColor.getARGB(); | ||
List< Pair< String, Integer > > tagColors = new ArrayList<>( Collections.nCopies( 2 * n + 1, null ) ); | ||
tagColors.set( 0, Pair.of( "default", defaultColor ) ); | ||
for ( int i = 0; i < n; i++ ) | ||
{ | ||
int color = ColorUtils.mixColors( highlightColor, defaultColor, ( float ) i / n ); | ||
String label = "T-" + ( i + 1 ); | ||
tagColors.set( n - i, Pair.of( label, color ) ); | ||
} | ||
for ( int i = 0; i < n; i++ ) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is maybe unnecessarily the same - symmetrical, but it makes no sense to optimize it either unless you maybe want to offer a transition from mother-default, over division, to daughters-new-default colors There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, the colors are very similar. But having different tags for before and after the cell division allows the user to manually assign a different color before the cell division. If they would want to. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that's a good and very valid point... (to have explicit Tags for the colors before and after the division so that the user can later change them...) |
||
{ | ||
int color = ColorUtils.mixColors( highlightColor, defaultColor, ( float ) i / n ); | ||
String label = "T" + ( i + 1 ); | ||
tagColors.set( n + i + 1, Pair.of( label, color ) ); | ||
} | ||
TagSetStructure.TagSet tagset = TagSetUtils.addNewTagSetToModel( model, tagSetName, tagColors ); | ||
List< TagSetStructure.Tag > allTags = tagset.getTags(); | ||
backgroundTag = allTags.get( 0 ); | ||
tagsBefore = IntStream.range( 0, n ).mapToObj( i -> allTags.get( n - i ) ).collect( Collectors.toList() ); | ||
tagsAfter = IntStream.range( 0, n ).mapToObj( i -> allTags.get( n + i + 1 ) ).collect( Collectors.toList() ); | ||
} | ||
|
||
private void applyTags() | ||
{ | ||
ModelBranchGraph branchGraph = model.getBranchGraph(); | ||
for ( BranchSpot branch : branchGraph.vertices() ) | ||
{ | ||
int start = branch.getFirstTimePoint(); | ||
int end = branch.getTimepoint(); | ||
boolean dividesBefore = !branch.incomingEdges().isEmpty(); | ||
boolean dividesAfter = !branch.outgoingEdges().isEmpty(); | ||
Iterator< Spot > iterator = branchGraph.vertexBranchIterator( branch ); | ||
while ( iterator.hasNext() ) | ||
{ | ||
Spot spot = iterator.next(); | ||
int time = spot.getTimepoint(); | ||
int startOffset = time - start; | ||
int endOffset = end - time; | ||
TagSetStructure.Tag tag = getTag( dividesBefore, dividesAfter, startOffset, endOffset ); | ||
model.getTagSetModel().getVertexTags().set( spot, tag ); | ||
} | ||
branchGraph.releaseIterator( iterator ); | ||
} | ||
} | ||
|
||
private TagSetStructure.Tag getTag( boolean dividesBefore, | ||
boolean dividesAfter, int startOffset, int endOffset ) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could be on the same line as the line above. |
||
{ | ||
if ( dividesAfter && ( endOffset < tagsBefore.size() ) ) | ||
return tagsBefore.get( endOffset ); | ||
if ( dividesBefore && ( startOffset < tagsAfter.size() ) ) | ||
return tagsAfter.get( startOffset ); | ||
return backgroundTag; | ||
} | ||
|
||
@Override | ||
public boolean isCanceled() | ||
{ | ||
return false; | ||
} | ||
|
||
@Override | ||
public void cancel( String reason ) | ||
{ | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could consider adding a comment, why this method is intentionally empty. |
||
} | ||
|
||
@Override | ||
public String getCancelReason() | ||
{ | ||
return null; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
/*- | ||
* #%L | ||
* mastodon-tomancak | ||
* %% | ||
* Copyright (C) 2018 - 2024 Tobias Pietzsch | ||
* %% | ||
* Redistribution and use in source and binary forms, with or without | ||
* modification, are permitted provided that the following conditions are met: | ||
* | ||
* 1. Redistributions of source code must retain the above copyright notice, | ||
* this list of conditions and the following disclaimer. | ||
* 2. Redistributions in binary form must reproduce the above copyright notice, | ||
* this list of conditions and the following disclaimer in the documentation | ||
* and/or other materials provided with the distribution. | ||
* | ||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE | ||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
* POSSIBILITY OF SUCH DAMAGE. | ||
* #L% | ||
*/ | ||
package org.mastodon.mamut.tomancak.divisiontagset; | ||
|
||
import static org.mastodon.app.ui.ViewMenuBuilder.item; | ||
import static org.mastodon.app.ui.ViewMenuBuilder.menu; | ||
|
||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
import org.mastodon.app.ui.ViewMenuBuilder; | ||
import org.mastodon.mamut.KeyConfigScopes; | ||
import org.mastodon.mamut.ProjectModel; | ||
import org.mastodon.mamut.plugin.MamutPlugin; | ||
import org.mastodon.ui.keymap.KeyConfigContexts; | ||
import org.scijava.command.CommandService; | ||
import org.scijava.plugin.Parameter; | ||
import org.scijava.plugin.Plugin; | ||
import org.scijava.ui.behaviour.io.gui.CommandDescriptionProvider; | ||
import org.scijava.ui.behaviour.io.gui.CommandDescriptions; | ||
import org.scijava.ui.behaviour.util.AbstractNamedAction; | ||
import org.scijava.ui.behaviour.util.Actions; | ||
import org.scijava.ui.behaviour.util.RunnableAction; | ||
|
||
/** | ||
* A Mastodon plugin that adds a tag set for highlighting cell divisions. | ||
* <p> | ||
* This class executes the {@link CellDivisionsTagSetCommand} when the | ||
* menu item is clicked. | ||
*/ | ||
@Plugin( type = MamutPlugin.class ) | ||
public class CellDivisionsTagSetPlugin implements MamutPlugin | ||
{ | ||
@Parameter | ||
private CommandService commandService; | ||
|
||
private static final String ID = "[tomancak] create cell divisions tag set"; | ||
|
||
private static final String[] KEYS = { "not mapped" }; | ||
|
||
private static final Map< String, String > menuTexts = Collections.singletonMap( ID, "Add Tag Set to Highlight Cell Divisions ..." ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I recommend to omit the "..." at the end. This would be more consistent with other function that can be called from the menu, which also do not have the "..." |
||
|
||
private ProjectModel projectModel = null; | ||
|
||
@Plugin( type = CommandDescriptionProvider.class ) | ||
public static class Descriptions extends CommandDescriptionProvider | ||
{ | ||
public Descriptions() | ||
{ | ||
super( KeyConfigScopes.MAMUT, KeyConfigContexts.TRACKSCHEME, KeyConfigContexts.BIGDATAVIEWER ); | ||
} | ||
|
||
@Override | ||
public void getCommandDescriptions( CommandDescriptions descriptions ) | ||
{ | ||
descriptions.add( ID, KEYS, "Adds a tag set to highlight cell divisions." ); | ||
} | ||
} | ||
|
||
private final AbstractNamedAction action; | ||
|
||
public CellDivisionsTagSetPlugin() | ||
{ | ||
action = new RunnableAction( ID, () -> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is nice, I like the defensive style of it, makes the SW more robust.. |
||
if ( projectModel != null ) | ||
run(); | ||
} ); | ||
updateEnabledActions(); | ||
} | ||
|
||
@Override | ||
public void setAppPluginModel( ProjectModel projectModel ) | ||
{ | ||
this.projectModel = projectModel; | ||
updateEnabledActions(); | ||
} | ||
|
||
private void updateEnabledActions() | ||
{ | ||
action.setEnabled( projectModel != null ); | ||
} | ||
|
||
@Override | ||
public List< ViewMenuBuilder.MenuItem > getMenuItems() | ||
{ | ||
return Collections.singletonList( menu( "Plugins", menu( "Tags", item( ID ) ) ) ); | ||
} | ||
|
||
@Override | ||
public Map< String, String > getMenuTexts() | ||
{ | ||
return menuTexts; | ||
} | ||
|
||
@Override | ||
public void installGlobalActions( Actions actions ) | ||
{ | ||
actions.namedAction( action, KEYS ); | ||
} | ||
|
||
private void run() | ||
{ | ||
commandService.run( CellDivisionsTagSetCommand.class, true, "projectModel", projectModel ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When this is called, on my machine, I get this exception:
|
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the users view, it is possible to have different tag set names for different settings. However, users may easily forget to do this.
If they do, they end up with this situation:
You could consider adding the timepoints setting to the tagset name automatically so that it is easier for the user afterwards to differentiate between different tagset.
E.g.: "highlight cell division (+/- 3 timepoints)"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I understand your point. But I would like to wait for user feedback. Having a predictable tag set name is also an advantage. There would also be the question if to include the colors in the tag set name and how, etc. Maybe overwriting existing tag sets is the best thing to do?