/*
* omeis.providers.re.ColorsFactory
*
* Copyright 2014 University of Dundee. All rights reserved.
* Use is subject to license terms supplied in LICENSE.txt
*/
package omeis.providers.re;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import ome.model.acquisition.Filter;
import ome.model.acquisition.FilterSet;
import ome.model.acquisition.Laser;
import ome.model.acquisition.LightPath;
import ome.model.acquisition.LightSource;
import ome.model.acquisition.TransmittanceRange;
import ome.model.core.Channel;
import ome.model.core.LogicalChannel;
import ome.model.units.Length;
/**
* Utility class to determine the color usually associated to a specified
* channel depending on its emission wavelength.
*
* @author Jean-Marie Burel <a
* href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
* @author <br>
* Andrea Falconi <a
* href="mailto:a.falconi@dundee.ac.uk"> a.falconi@dundee.ac.uk</a>
* @since OME2.2
*/
public class ColorsFactory {
/** Index of the red component of a color. */
public static final int RED_INDEX = 0;
/** Index of the red component of a color. */
public static final int GREEN_INDEX = 1;
/** Index of the red component of a color. */
public static final int BLUE_INDEX = 2;
/** Index of the red component of a color. */
public static final int ALPHA_INDEX = 3;
/** The Default value for the alpha component. */
static final int DEFAULT_ALPHA = 255;
/**
* Lower bound of the wavelength interval corresponding to a
* <code>BLUE</code> color.
*/
private static final int BLUE_MIN = 400;
/**
* Upper bound of the wavelength interval corresponding to a
* <code>BLUE</code> color and lower bound of the wavelength
* interval corresponding to a <code>GREEN</code> color.
*/
private static final int BLUE_TO_GREEN_MIN = 500;
/**
* Upper bound of the wavelength interval corresponding to a
* <code>GREEN</code> color and lower bound of the wavelength
* interval corresponding to a <code>RED</code> color.
*/
private static final int GREEN_TO_RED_MIN = 560;
/**
* Upper bound of the wavelength interval corresponding to a
* <code>RED</code> color.
*/
private static final int RED_MAX = 700;
/** The value to add to the cut-in to determine the color. */
private static final int RANGE = 15;
/**
* Returns <code>true</code> if the wavelength is in the blue
* color band, <code>false</code> otherwise.
*
* @param wavelength The wavelength to handle.
* @return See above.
*/
private static boolean rangeBlue(double wavelength) {
return wavelength < BLUE_TO_GREEN_MIN;
}
/**
* Returns <code>true</code> if the wavelength is in the green
* color band, <code>false</code> otherwise.
*
* @param wavelength The wavelength to handle.
* @return See above.
*/
private static boolean rangeGreen(double wavelength) {
return wavelength >= BLUE_TO_GREEN_MIN && wavelength < GREEN_TO_RED_MIN;
}
/**
* Returns <code>true</code> if the wavelength is in the red
* color band, <code>false</code> otherwise.
*
* @param wavelength The wavelength to handle.
* @return See above.
*/
private static boolean rangeRed(double wavelength) {
return wavelength >= GREEN_TO_RED_MIN;
}
/**
* Returns <code>true</code> if the channel has emission and/or excitation
* information, <code>false</code> otherwise.
*
* @param lc The channel to handle.
* @param full Pass <code>true</code> to check emission and excitation,
* <code>false</code> to only check emission.
* @return See above.
*/
private static boolean hasEmissionExcitationData(LogicalChannel lc,
boolean full)
{
if (lc == null) return false;
if (lc.getEmissionWave() != null) return true;
//Need to check the light path.
List<Filter> filters;
Iterator<Filter> j;
FilterSet f = null;
LightPath lp = null;
//light path first
if (lc.getLightPath() != null) {
lp = (LightPath) lc.getLightPath();
if (lp.sizeOfEmissionFilterLink() > 0) {
filters = new ArrayList<Filter>();
j = lp.linkedEmissionFilterIterator();
while (j.hasNext()) {
filters.add(j.next());
}
sortFilters(filters);
j = filters.iterator();
while (j.hasNext()) {
if (isFilterHasEmissionData(j.next())) return true;
}
}
}
if (lc.getFilterSet() != null) {
f = (FilterSet) lc.getFilterSet();
if (f.sizeOfEmissionFilterLink() > 0) {
filters = new ArrayList<Filter>();
j = f.linkedEmissionFilterIterator();
while (j.hasNext()) {
filters.add(j.next());
}
sortFilters(filters);
j = filters.iterator();
while (j.hasNext()) {
if (isFilterHasEmissionData(j.next())) return true;
}
}
}
if (!full) return false;
//Excitation
//Laser
if (lc.getLightSourceSettings() != null) {
LightSource src = lc.getLightSourceSettings().getLightSource();
if (src instanceof Laser) {
Laser laser = (Laser) src;
if (laser.getWavelength() != null) return true;
}
}
if (lc.getExcitationWave() != null) return true;
//light path
if (lp != null) {
if (lp.sizeOfExcitationFilterLink() > 0) {
filters = new ArrayList<Filter>();
j = lp.linkedExcitationFilterIterator();
while (j.hasNext()) {
filters.add(j.next());
}
sortFilters(filters);
j = filters.iterator();
while (j.hasNext()) {
if (isFilterHasEmissionData(j.next())) return true;
}
}
}
//filter set
if (f != null) {
if (f.sizeOfExcitationFilterLink() > 0) {
filters = new ArrayList<Filter>();
j = f.linkedExcitationFilterIterator();
while (j.hasNext()) {
filters.add(j.next());
}
sortFilters(filters);
j = filters.iterator();
while (j.hasNext()) {
if (isFilterHasEmissionData(j.next())) return true;
}
}
}
return false;
}
/**
* Determines the color usually associated to the specified
* wavelength or explicitly defined for a particular channel.
*
* @param channel The channel to determine the color for.
* @param lc The logical channel associated to that channel.
* @return An RGBA array representation of the color.
*/
private static int[] getColor(Channel channel, LogicalChannel lc) {
if (lc == null) return null;
if (!hasEmissionExcitationData(lc, true)) {
Integer red = channel.getRed();
Integer green = channel.getGreen();
Integer blue = channel.getBlue();
Integer alpha = channel.getAlpha();
if (red != null && green != null && blue != null && alpha != null) {
// We've got a color image of some type that has explicitly
// specified which channel is Red, Green, Blue or some other wacky
// color.
//if (red == 0 && green == 0 && blue == 0 && alpha == 0)
// alpha = DEFAULT_ALPHA;
return new int[] { red, green, blue, alpha };
}
return null;
}
Length valueWavelength = lc.getEmissionWave();
//First we check the emission wavelength.
if (valueWavelength != null) return determineColor(valueWavelength);
Length valueFilter = null;
//First check the emission filter.
//First check if filter
List<Filter> filters;
Iterator<Filter> j;
FilterSet f = null;
LightPath lp = null;
//LightPath
if (valueFilter == null && lc.getLightPath() != null) {
filters = new ArrayList<Filter>();
lp = lc.getLightPath();
j = lp.linkedEmissionFilterIterator();
while (j.hasNext()) {
filters.add(j.next());
}
sortFilters(filters);
while (valueFilter == null && j.hasNext()) {
valueFilter = getValueFromFilter(j.next(), true);
}
}
if (valueFilter == null && lc.getFilterSet() != null) {
filters = new ArrayList<Filter>();
f = lc.getFilterSet();
j = f.linkedEmissionFilterIterator();
while (j.hasNext()) {
filters.add(j.next());
}
sortFilters(filters);
while (valueFilter == null && j.hasNext()) {
valueFilter = getValueFromFilter(j.next(), true);
}
}
//Laser
if (valueFilter == null && lc.getLightSourceSettings() != null) {
LightSource ls = lc.getLightSourceSettings().getLightSource();
if (ls instanceof Laser) valueWavelength = ((Laser) ls).getWavelength();
}
if (valueWavelength != null) return determineColor(valueWavelength);
//Excitation
valueWavelength = lc.getExcitationWave();
if (valueWavelength != null) return determineColor(valueWavelength);
//light path first
if (valueFilter == null && lp != null) {
filters = new ArrayList<Filter>();
j = lp.linkedExcitationFilterIterator();
while (j.hasNext()) {
filters.add(j.next());
}
sortFilters(filters);
while (valueFilter == null && j.hasNext()) {
valueFilter = getValueFromFilter(j.next(), false);
}
}
if (valueFilter == null && f != null) {
filters = new ArrayList<Filter>();
j = f.linkedExcitationFilterIterator();
while (j.hasNext()) {
filters.add(j.next());
}
sortFilters(filters);
while (valueFilter == null && j.hasNext()) {
valueFilter = getValueFromFilter(j.next(), false);
}
}
return determineColor(valueFilter != null ? valueFilter : null);
}
/**
* Determines the color corresponding to the passed value.
*
* @param value The value to handle.
* @return
*/
private static int[] determineColor(Length value)
{
if (value == null) return null;
if (rangeBlue(value.getValue())) return newBlueColor();
if (rangeGreen(value.getValue())) return newGreenColor();
if (rangeRed(value.getValue())) return newRedColor();
return null;
}
/**
* Returns the range of the wavelength or <code>null</code>.
*
* @param filter The filter to handle.
* @param emission Passed <code>true</code> to indicate that the filter is
* an emission filter, <code>false</code> otherwise.
* @return See above.
*/
private static Length getValueFromFilter(Filter filter, boolean emission)
{
if (filter == null) return null;
TransmittanceRange transmittance = filter.getTransmittanceRange();
if (transmittance == null) return null;
Length cutIn = transmittance.getCutIn();
if (emission) {
if (cutIn == null) return null;
return new Length(cutIn.getValue()+RANGE, cutIn.getUnit());
}
Length cutOut = transmittance.getCutOut();
if (cutOut == null) return null;
if (cutIn == null || cutIn.getValue() == 0)
cutIn = new Length(cutOut.getValue()-2*RANGE, cutOut.getUnit());
// FIXME: are these in the same unit?
Length v = new Length((cutIn.getValue()+cutOut.getValue()/2), cutOut.getUnit());
if (v.getValue() < 0) return new Length(0, cutOut.getUnit());
return v;
}
/**
* Returns <code>true</code> if the channel has emission metadata,
* <code>false</code> otherwise.
*
* @param f The filter to handle.
* @return See above.
*/
private static boolean isFilterHasEmissionData(Filter f)
{
if (f == null) return false;
TransmittanceRange transmittance = f.getTransmittanceRange();
if (transmittance == null) return false;
return transmittance.getCutIn() != null;
}
/**
* Sorts the filters by ID to make sure that the filters with the
* highest ID is first picked.
*
* @param filters the filters to handle.
*/
private static void sortFilters(List<Filter> filters)
{
if (filters == null || filters.size() == 0) return;
Comparator c = new Comparator() {
public int compare(Object o1, Object o2)
{
long id1 = ((Filter) o1).getId(),
id2 = ((Filter) o2).getId();
int v = 0;
if (id1 < id2) v = -1;
else if (id1 > id2) v = 1;
return v;
}
};
Collections.sort(filters, c);
}
/**
* Determines the color usually associated to the specified wavelength.
*
* @param index The channel index.
* @param channel The channel to determine the color for.
* @return A color.
*/
public static int[] getColor(int index, Channel channel) {
return getColor(index, channel, channel.getLogicalChannel());
}
/**
* Determines the color usually associated to the specified wavelength.
*
* @param index The channel index.
* @param channel The channel to determine the color for.
* @param lc The entity hosting information about the emission etc.
* @return A color.
*/
public static int[] getColor(int index, Channel channel, LogicalChannel
lc) {
if (lc == null) lc = channel.getLogicalChannel();
int[] c = ColorsFactory.getColor(channel, lc);
if (c != null) return c;
switch (index) {
case 0: return newRedColor();
case 1: return newGreenColor();
default: return newBlueColor();
/*
case 1: return newBlueColor();
default: return newGreenColor();
*/
}
}
/**
* Returns <code>true</code> if the channel has emission metadata,
* <code>false</code> otherwise.
*
* @param lc The channel to handle.
* @return See above.
*/
public static boolean hasEmissionData(LogicalChannel lc)
{
return hasEmissionExcitationData(lc, false);
}
/**
* Creates a new <i>Red</i> Color object.
*
* @return An RGBA array representation of the color Red.
*/
public static int[] newRedColor() {
return new int[] { 255, 0, 0, DEFAULT_ALPHA };
}
/**
* Creates a new <i>Green</i> Color object.
*
* @return An RGBA array representation of the color Green.
*/
public static int[] newGreenColor() {
return new int[] { 0, 255, 0, DEFAULT_ALPHA };
}
/**
* Creates a new <i>Blue</i> Color object.
*
* @return An RGBA array representation of the color Blue.
*/
public static int[] newBlueColor() {
return new int[] { 0, 0, 255, DEFAULT_ALPHA };
}
/**
* Creates a new <i>Grey</i> Color object.
*
* @return An RGBA array representation of the color Blue.
*/
public static int[] newGreyColor() {
return new int[] { 128, 128, 128, DEFAULT_ALPHA };
}
/**
* Creates a new <i>White</i> Color object.
*
* @return An RGBA array representation of the color Blue.
*/
public static int[] newWhiteColor() {
return new int[] { 255, 255, 255, DEFAULT_ALPHA };
}
}