-
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
Random coloring of lineage trees only for vizu enhancement #29
base: master
Are you sure you want to change the base?
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,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"; | ||||||||
|
||||||||
final static private String COLORS_16 = "Create and use a new tagset (16 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. Reorder the modifiers to comply with the Java Language Specification
Suggested change
|
||||||||
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() { | ||||||||
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.
Suggested change
|
||||||||
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); | ||||||||
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. 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. |
||||||||
{ | ||||||||
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.
Suggested change
|
||||||||
// 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, | ||||||||
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 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; | ||||||||
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 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; | ||||||||
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.
Suggested change
|
||||||||
|
||||||||
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)) | ||||||||
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 you use a different name here? |
||||||||
.findFirst(); | ||||||||
if (!ts.isPresent()) | ||||||||
throw new IllegalStateException("Requested tagset '"+colorScheme+"' was not found now."); | ||||||||
chosenTagSet = ts.get(); | ||||||||
} | ||||||||
|
||||||||
return chosenTagSet; | ||||||||
} | ||||||||
|
||||||||
|
||||||||
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.
Suggested change
|
||||||||
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(); | ||||||||
} | ||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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
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.
|
||
/** | ||
* Add a new tag set to the given model. | ||
* @param model The model that will contain the new tag set. | ||
|
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.
Field may be final