Skip to content
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

Random coloring of lineage trees only for vizu enhancement #29

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 178 additions & 0 deletions src/main/java/org/mastodon/mamut/tomancak/LineageRandomColorizer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/*-
* #%L
* mastodon-tomancak
* %%
* Copyright (C) 2023 Vladimir Ulman
* %%
* 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;

import org.mastodon.mamut.model.Spot;
import org.mastodon.mamut.plugin.MamutPluginAppModel;
import org.mastodon.mamut.tomancak.util.SpotsIterator;
import org.mastodon.model.tag.ObjTags;
import org.mastodon.model.tag.TagSetStructure;
import org.scijava.command.Command;
import org.scijava.command.DynamicCommand;
import org.scijava.log.LogService;
import org.scijava.log.Logger;
import org.scijava.plugin.Parameter;
import org.scijava.plugin.Plugin;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;

import static org.mastodon.mamut.tomancak.lineage_registration.TagSetUtils.addNewTagSetToModel;
import static org.mastodon.mamut.tomancak.lineage_registration.TagSetUtils.rgbToValidColor;

@Plugin( type = Command.class, name = "Random colorize lineages" )
public class LineageRandomColorizer extends DynamicCommand {
@Parameter(persist = false)
private MamutPluginAppModel pluginAppModel;

@Parameter
private LogService logService;

@Parameter(label = "Choose color scheme:", initializer = "readAvailColors", choices = {})
private String colorScheme = "Create new tagset";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Field may be final


final static private String COLORS_16 = "Create and use a new tagset (16 colors)";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reorder the modifiers to comply with the Java Language Specification

Suggested change
final static private String COLORS_16 = "Create and use a new tagset (16 colors)";
private static final String COLORS_16 = "Create and use a new tagset (16 colors)";

final static private String COLORS_64 = "Create and use a new tagset (64 colors)";

// sets up the drop-down box in the dialog with two create-tagset-items
// followed by the list of the currently available tagsets
private void readAvailColors() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private void readAvailColors() {
@SuppressWarnings( "unused" )
private void readAvailColors() {

List<String> choices = new ArrayList<>(50);

pluginAppModel.getAppModel().getModel()
.getTagSetModel()
.getTagSetStructure()
.getTagSets()
.forEach( ts -> choices.add( "Use existing "+ts.getName() ) );

//finally, also allow to create new tag sets (color palettes)
choices.add( COLORS_16 );
choices.add( COLORS_64 );

this.getInfo().getMutableInput( "colorScheme", String.class ).setChoices( choices );
}

static final Map<String, Integer> mokolePaletteOf16Colors = new HashMap<>(16);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding this Palette to this class: https://github.com/mastodon-sc/mastodon/blob/dev/src/main/java/org/mastodon/util/ColorUtils.java

ColorUtils contains the Glasbey Palette already. I think it would be useful, if different color palettes would be in the same class or at least near to each other.

{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
{
static {

// used https://mokole.com/palette.html with the settings:
// 16 colors, 5% min luminosity, 90% max luminosity, 5000 max loops
mokolePaletteOf16Colors.put( "darkslategray", rgbToValidColor(0x2f4f4f) );
mokolePaletteOf16Colors.put( "saddlebrown", rgbToValidColor(0x8b4513) );
mokolePaletteOf16Colors.put( "darkgreen", rgbToValidColor(0x006400) );
mokolePaletteOf16Colors.put( "darkkhaki", rgbToValidColor(0xbdb76b) );
mokolePaletteOf16Colors.put( "navy", rgbToValidColor(0x000080) );
mokolePaletteOf16Colors.put( "mediumturquoise", rgbToValidColor(0x48d1cc) );
mokolePaletteOf16Colors.put( "red", rgbToValidColor(0xff0000) );
mokolePaletteOf16Colors.put( "orange", rgbToValidColor(0xffa500) );
mokolePaletteOf16Colors.put( "yellow", rgbToValidColor(0xffff00) );
mokolePaletteOf16Colors.put( "lime", rgbToValidColor(0x00ff00) );
mokolePaletteOf16Colors.put( "mediumspringgreen", rgbToValidColor(0x00fa9a) );
mokolePaletteOf16Colors.put( "blue", rgbToValidColor(0x0000ff) );
mokolePaletteOf16Colors.put( "orchid", rgbToValidColor(0xda70d6) );
mokolePaletteOf16Colors.put( "fuchsia", rgbToValidColor(0xff00ff) );
mokolePaletteOf16Colors.put( "dodgerblue", rgbToValidColor(0x1e90ff) );
mokolePaletteOf16Colors.put( "lightpink", rgbToValidColor(0xffb6c1) );
}

private TagSetStructure.TagSet createCoolSmallTagSet() {
return addNewTagSetToModel( pluginAppModel.getAppModel().getModel(),
"Palette of 16 colors", mokolePaletteOf16Colors.entrySet() );
}


private TagSetStructure.TagSet createNewTagSet(final int rColors,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The three parameters of this method are always set to 4 each. Could they be removed?

final int gColors,
final int bColors) {
final int colorsInTotal = rColors*gColors*bColors;
final Map<String, Integer> palette = new HashMap<>(colorsInTotal);

final int rStep = 256 / rColors; //relying on down-rounding
final int gStep = 256 / gColors;
final int bStep = 256 / bColors;

for (int rColor = 0; rColor < rColors; ++rColor)
for (int gColor = 0; gColor < gColors; ++gColor)
for (int bColor = 0; bColor < bColors; ++bColor) {
int color = ((rColor*rStep) << 16) + ((gColor*gStep) << 8) + bColor*bStep;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line looks a bit as it could be rewritten using one of the existing rgbToValidColor() methods. Could you check this?

palette.put("RGB "+(rColor*rStep)+","
+(gColor*gStep)+","+(bColor*bStep), rgbToValidColor(color) );
}
return addNewTagSetToModel( pluginAppModel.getAppModel().getModel(),
"Palette of "+colorsInTotal+" colors", palette.entrySet() );
}


private TagSetStructure.TagSet getChosenTagSet() {
TagSetStructure.TagSet chosenTagSet = null;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
TagSetStructure.TagSet chosenTagSet = null;
TagSetStructure.TagSet chosenTagSet;


if (colorScheme.equals(COLORS_16)) chosenTagSet = createCoolSmallTagSet();
else if (colorScheme.equals(COLORS_64)) chosenTagSet = createNewTagSet(4,4,4);
else {
Optional<TagSetStructure.TagSet> ts = pluginAppModel.getAppModel().getModel()
.getTagSetModel()
.getTagSetStructure()
.getTagSets()
.stream()
.filter(_ts -> _ts.getName().equals(colorScheme))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you use a different name here?
Variable names starting with "_" do not match the naming conventions of Java.

.findFirst();
if (!ts.isPresent())
throw new IllegalStateException("Requested tagset '"+colorScheme+"' was not found now.");
chosenTagSet = ts.get();
}

return chosenTagSet;
}


Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@Override

public void run()
{
pluginAppModel.getAppModel().getModel().getTagSetModel().pauseListeners();

final TagSetStructure.TagSet chosenTagSet = getChosenTagSet();
AtomicInteger currentColorIdx = new AtomicInteger( 0 );

final ObjTags<Spot> colorizer = pluginAppModel.getAppModel().getModel().getTagSetModel().getVertexTags();
final Logger dedicatedLog = logService.subLogger( "Coloring of lineage trees" );

final SpotsIterator visitor = new SpotsIterator( pluginAppModel.getAppModel(), dedicatedLog );
visitor.visitRootsFromEntireGraph( root -> {
TagSetStructure.Tag color = chosenTagSet.getTags().get( currentColorIdx.get() );
currentColorIdx.set( (currentColorIdx.get()+1) % chosenTagSet.getTags().size() );

visitor.visitDownstreamSpots( root, spot -> colorizer.set( spot, color ) );
} );
dedicatedLog.info("Done with the random coloring.");

pluginAppModel.getAppModel().getModel().getTagSetModel().resumeListeners();
}
}
17 changes: 17 additions & 0 deletions src/main/java/org/mastodon/mamut/tomancak/TomancakPlugins.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ public class TomancakPlugins extends AbstractContextual implements MamutPlugin
private static final String EXPORTS_SPOTS_COUNTS = "[tomancak] export spots counts";
private static final String MERGE_PROJECTS = "[tomancak] merge projects";
private static final String TWEAK_DATASET_PATH = "[tomancak] fix project image path";
private static final String RANDOM_COLOR_TREES = "[tomancak] random color trees";

private static final String[] EXPORT_PHYLOXML_KEYS = { "not mapped" };
private static final String[] FLIP_DESCENDANTS_KEYS = { "ctrl E" };
Expand All @@ -101,6 +102,7 @@ public class TomancakPlugins extends AbstractContextual implements MamutPlugin
private static final String[] EXPORTS_SPOTS_COUNTS_KEYS = { "not mapped" };
private static final String[] MERGE_PROJECTS_KEYS = { "not mapped" };
private static final String[] TWEAK_DATASET_PATH_KEYS = { "not mapped" };
private static final String[] RANDOM_COLOR_TREES_KEYS = { "not mapped" };

private static Map< String, String > menuTexts = new HashMap<>();

Expand All @@ -121,6 +123,7 @@ public class TomancakPlugins extends AbstractContextual implements MamutPlugin
menuTexts.put( EXPORTS_SPOTS_COUNTS, "Export Spots Counts" );
menuTexts.put( MERGE_PROJECTS, "Merge Two Projects" );
menuTexts.put( TWEAK_DATASET_PATH, "Fix Image Path" );
menuTexts.put( RANDOM_COLOR_TREES, "Random Color Lineages" );
}

/*
Expand Down Expand Up @@ -152,6 +155,7 @@ public void getCommandDescriptions( final CommandDescriptions descriptions )
descriptions.add( EXPORTS_SPOTS_COUNTS, EXPORTS_SPOTS_COUNTS_KEYS, "Exports counts of spots into CSV-like files to be imported in data processors." );
descriptions.add( MERGE_PROJECTS, MERGE_PROJECTS_KEYS, "Merge two Mastodon projects into one." );
descriptions.add( TWEAK_DATASET_PATH, TWEAK_DATASET_PATH_KEYS, "Allows to insert new path to the BDV data and whether it is relative or absolute." );
descriptions.add( RANDOM_COLOR_TREES, RANDOM_COLOR_TREES_KEYS, "Assign to every lineage tree a randomly chosen color from the selected tag set." );
}
}

Expand Down Expand Up @@ -185,6 +189,8 @@ public void getCommandDescriptions( final CommandDescriptions descriptions )

private final AbstractNamedAction tweakDatasetPathAction;

private final AbstractNamedAction randomColorTreesAction;

private MamutPluginAppModel pluginAppModel;

public TomancakPlugins()
Expand All @@ -204,6 +210,7 @@ public TomancakPlugins()
exportSpotsCountsAction = new RunnableAction( EXPORTS_SPOTS_COUNTS, this::exportCounts );
mergeProjectsAction = new RunnableAction( MERGE_PROJECTS, this::mergeProjects );
tweakDatasetPathAction = new RunnableAction( TWEAK_DATASET_PATH, this::tweakDatasetPath );
randomColorTreesAction = new RunnableAction( RANDOM_COLOR_TREES, this::randomColorTrees );
updateEnabledActions();
}

Expand All @@ -220,6 +227,7 @@ public List< ViewMenuBuilder.MenuItem > getMenuItems()
return Arrays.asList(
menu( "Plugins",
item( COPY_TAG ),
item( RANDOM_COLOR_TREES ),
menu( "Auxiliary Displays",
item( COMPACT_LINEAGE_VIEW )),
menu( "Trees Management",
Expand Down Expand Up @@ -264,6 +272,7 @@ public void installGlobalActions( final Actions actions )
actions.namedAction( exportSpotsCountsAction, EXPORTS_SPOTS_COUNTS_KEYS );
actions.namedAction( mergeProjectsAction, MERGE_PROJECTS_KEYS );
actions.namedAction( tweakDatasetPathAction, TWEAK_DATASET_PATH_KEYS );
actions.namedAction( randomColorTreesAction, RANDOM_COLOR_TREES_KEYS );
}

private void updateEnabledActions()
Expand All @@ -283,6 +292,7 @@ private void updateEnabledActions()
exportSpotsCountsAction.setEnabled( appModel != null );
mergeProjectsAction.setEnabled( appModel != null );
tweakDatasetPathAction.setEnabled( appModel != null );
randomColorTreesAction.setEnabled( appModel != null );
}

private void exportPhyloXml()
Expand Down Expand Up @@ -413,4 +423,11 @@ private void labelSpotsSystematically()
LabelSpotsSystematicallyDialog.showDialog( pluginAppModel.getAppModel() );
}
}

private void randomColorTrees()
{
this.getContext().getService(CommandService.class).run(
LineageRandomColorizer.class, true,
"pluginAppModel", pluginAppModel);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,44 @@ private TagSetUtils()
// prevent instantiation of utility class
}

/**
* Help creating a visible color value by adding
* a (full opacity) alpha channel.
* @param rgbAsBottom24bits The encoded RGB triplet.
* @return The int value of the RGB color, ready to use
* with {@link org.mastodon.model.tag.TagSetStructure.Tag}.
*/
static public int rgbToValidColor(final int rgbAsBottom24bits) {
return 0xFF000000 | rgbAsBottom24bits;
}

/**
* Help creating a visible color value by adding
* a (full opacity) alpha channel.
* @param r 0-255 valued red channel.
* @param g 0-255 valued green channel.
* @param b 0-255 valued blue channel.
* @return The int value of the RGB color, ready to use
* with {@link org.mastodon.model.tag.TagSetStructure.Tag}.
*/
static public int rgbToValidColor(final int r, final int g, final int b) {
return 0xFF000000 | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF);
}

/**
* Help creating a fully-described color value.
* @param r 0-255 valued red channel.
* @param g 0-255 valued green channel.
* @param b 0-255 valued blue channel.
* @param alpha 0-255 valued opacity (alpha) channel,
* 0 - fully transparent, 255 - fully opaque.
* @return The int value of the RGB color, ready to use
* with {@link org.mastodon.model.tag.TagSetStructure.Tag}.
*/
static public int rgbaToValidColor(final int r, final int g, final int b, final int alpha) {
return ((alpha & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF);
}

Comment on lines +22 to +59
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/**
* Add a new tag set to the given model.
* @param model The model that will contain the new tag set.
Expand Down