/* * ome.formats.model.ChannelProcessor * *------------------------------------------------------------------------------ * Copyright (C) 2006-2008 University of Dundee. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *------------------------------------------------------------------------------ */ package ome.formats.model; import static omero.rtypes.rint; import static omero.rtypes.rstring; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import loci.formats.FormatTools; import loci.formats.IFormatReader; import ome.util.LSID; import omero.RInt; import omero.RString; import omero.model.Channel; import omero.model.Filter; import omero.model.Image; import omero.model.Laser; import omero.model.Length; import omero.model.LightSource; import omero.model.LogicalChannel; import omero.model.Pixels; import omero.model.TransmittanceRange; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.math.DoubleMath; /** * Processes the pixels sets of an IObjectContainerStore and ensures * that LogicalChannel containers are present in the container cache, and * populating channel name and colour where appropriate. * * @author Chris Allan <callan at blackcat dot ca> * @author Jean-Marie <jburel at dundee dot ac dot uk> * */ public class ChannelProcessor implements ModelProcessor { /** Name of the <code>red</code> component when it is a graphics image. */ public static final String RED_TEXT = "Red"; /** Name of the <code>green</code> component when it is a graphics image. */ public static final String GREEN_TEXT = "Green"; /** Name of the <code>blue</code> component when it is a graphics image. */ public static final String BLUE_TEXT = "Blue"; /** Name of the <code>alpha</code> component when it is a graphics image. */ public static final String ALPHA_TEXT = "Alpha"; /** Logger for this class */ private Logger log = LoggerFactory.getLogger(ChannelProcessor.class); /** Container store we're currently working with. */ private IObjectContainerStore store; /** Bio-Formats reader implementation we're currently working with. */ private IFormatReader reader; /** * Returns the name from the wavelength. * * @param value The value to handle. * @return See above. */ private String getNameFromWavelength(Length value) { if (value == null) return null; //Check that the value is an int if (DoubleMath.isMathematicalInteger(value.getValue())) { return ""+value.getValue(); } return value.toString(); } /** * Sets the default color if it is a single channel image. * * @param channelData Channel data to use to set the color. */ private void setSingleChannel(ChannelData channelData) { int channelIndex = channelData.getChannelIndex(); Channel channel = channelData.getChannel(); Integer red = getValue(channel.getRed()); Integer green = getValue(channel.getGreen()); Integer blue = getValue(channel.getBlue()); Integer alpha = getValue(channel.getAlpha()); RString name; //color already set by Bio-formats if (red != null && green != null && blue != null && alpha != null) { return; } int[] defaultColor = ColorsFactory.newGreyColor(); channel.setRed(rint(defaultColor[ColorsFactory.RED_INDEX])); channel.setGreen(rint(defaultColor[ColorsFactory.GREEN_INDEX])); channel.setBlue(rint(defaultColor[ColorsFactory.BLUE_INDEX])); channel.setAlpha(rint(defaultColor[ColorsFactory.ALPHA_INDEX])); } /** * Populates the default color for the channel if one does not already * exist. * * @param channelData Channel data to use to set the color. * @param isGraphicsDomaind Whether or not the image is in the graphics * domain according to Bio-Formats. */ private void populateDefault( ChannelData channelData, boolean isGraphicsDomain) { int channelIndex = channelData.getChannelIndex(); Channel channel = channelData.getChannel(); LogicalChannel lc = channelData.getLogicalChannel(); int[] defaultColor; if (isGraphicsDomain) { log.debug("Setting color channel to RGB."); setDefaultChannelColor(channel, channelIndex); switch (channelIndex) { case 0: //red if (lc.getName() == null) lc.setName(rstring(RED_TEXT)); break; case 1: //green if (lc.getName() == null) lc.setName(rstring(GREEN_TEXT)); break; case 2: //blue if (lc.getName() == null) lc.setName(rstring(BLUE_TEXT)); break; case 3: //alpha, reset transparent channel.setRed(rint(0)); channel.setGreen(rint(0)); channel.setBlue(rint(0)); channel.setAlpha(rint(0)); //transparent if (lc.getName() == null) lc.setName(rstring(ALPHA_TEXT)); } return; } Integer red = getValue(channel.getRed()); Integer green = getValue(channel.getGreen()); Integer blue = getValue(channel.getBlue()); Integer alpha = getValue(channel.getAlpha()); RString name; //color already set by Bio-formats if (red != null && green != null && blue != null && alpha != null) { //Try to set the name. log.debug("Already set in BF."); if (lc.getName() == null) { name = getChannelName(channelData); if (name != null) lc.setName(name); } return; } //not set by //First we check the emission wavelength. Length valueWavelength = lc.getEmissionWave(); if (valueWavelength != null) { setChannelColor( channel, channelIndex, ColorsFactory.determineColor(valueWavelength)); if (lc.getName() == null) { lc.setName(rstring(getNameFromWavelength(valueWavelength))); } return; } Length valueFilter = null; //First check the emission filter. //First check if filter Filter f = getValidFilter(channelData.getLightPathEmissionFilters(), true); if (f != null) valueFilter = ColorsFactory.getValueFromFilter(f, true); if (valueFilter != null) { setChannelColor( channel, channelIndex, ColorsFactory.determineColor(valueFilter)); if (lc.getName() == null) { name = getNameFromFilter(f); if (name != null) lc.setName(name); } return; } f = channelData.getFilterSetEmissionFilter(); valueFilter = ColorsFactory.getValueFromFilter(f, true); if (valueFilter != null) { setChannelColor( channel, channelIndex, ColorsFactory.determineColor(valueFilter)); if (lc.getName() == null) { name = getNameFromFilter(f); if (name != null) lc.setName(name); } return; } //Laser if (channelData.getLightSource() != null) { LightSource ls = channelData.getLightSource(); if (ls instanceof Laser) { valueWavelength = ((Laser) ls).getWavelength(); if (valueWavelength != null) { setChannelColor( channel, channelIndex, ColorsFactory.determineColor(valueWavelength)); if (lc.getName() == null) { lc.setName(rstring(getNameFromWavelength(valueWavelength))); } return; } } } //Excitation valueWavelength = lc.getExcitationWave(); if (valueWavelength != null) { setChannelColor( channel, channelIndex, ColorsFactory.determineColor(valueWavelength)); if (lc.getName() == null) { lc.setName(rstring(getNameFromWavelength(valueWavelength))); } return; } f = getValidFilter(channelData.getLightPathExcitationFilters(), false); if (f != null) valueFilter = ColorsFactory.getValueFromFilter(f, false); if (valueFilter != null) { setChannelColor( channel, channelIndex, ColorsFactory.determineColor(valueFilter)); if (lc.getName() == null) { name = getNameFromFilter(f); if (name != null) lc.setName(name); } return; } f = channelData.getFilterSetExcitationFilter(); valueFilter = ColorsFactory.getValueFromFilter(f, false); if (valueFilter != null) { setChannelColor( channel, channelIndex, ColorsFactory.determineColor(valueFilter)); if (lc.getName() == null) { name = getNameFromFilter(f); if (name != null) lc.setName(name); } return; } //not been able to set the color setDefaultChannelColor(channel, channelIndex); } /** * Returns the first filter from the list with a <code>not null</code> * value. * * @param filters The collection 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 Filter getValidFilter(List<Filter> filters, boolean emission) { if (filters == null) return null; Iterator<Filter> i = filters.iterator(); Length value = null; Filter f; while (i.hasNext()) { f = i.next(); value = ColorsFactory.getValueFromFilter(f, emission); if (value != null) return f; } return null; } /** * Sets the default color of the channel. * * @param channel The channel to handle. * @param index The index of the channel. */ private void setDefaultChannelColor(Channel channel, int index) { //not been able to set the color int[] defaultColor; switch (index) { case 0: //red defaultColor = ColorsFactory.newRedColor(); break; case 1: //green defaultColor = ColorsFactory.newGreenColor(); break; default: //blue defaultColor = ColorsFactory.newBlueColor(); } channel.setRed(rint(defaultColor[ColorsFactory.RED_INDEX])); channel.setGreen(rint(defaultColor[ColorsFactory.GREEN_INDEX])); channel.setBlue(rint(defaultColor[ColorsFactory.BLUE_INDEX])); channel.setAlpha(rint(defaultColor[ColorsFactory.ALPHA_INDEX])); } /** * Sets the color of the channel. * * @param channel The channel to handle. * @param index The index of the channel. * @param rgba The color to set. */ private void setChannelColor(Channel channel, int index, int[] rgba) { if (rgba == null) { setDefaultChannelColor(channel, index); return; } channel.setRed(rint(rgba[0])); channel.setGreen(rint(rgba[1])); channel.setBlue(rint(rgba[2])); channel.setAlpha(rint(rgba[3])); } /** * Returns a channel name string from a given filter. * * @param filter Filter to retrieve a channel name from. * @return See above. */ private RString getNameFromFilter(Filter filter) { if (filter == null) { return null; } TransmittanceRange t = filter.getTransmittanceRange(); return t == null? null : rstring(String.valueOf(t.getCutIn().getValue())); } /** * Returns the concrete value of an OMERO rtype. * * @param value OMERO rtype to get the value of. * @return Concrete value of <code>value</code> or <code>null</code> if * <code>value == null</code>. */ private Integer getValue(RInt value) { return value == null? null : value.getValue(); } /** * Determines the name of the channel. * This method should only be invoked when a color was assigned by * Bio-formats. * * @param channelData The channel to handle. * @return See above. */ private RString getChannelName(ChannelData channelData) { LogicalChannel lc = channelData.getLogicalChannel(); Length value = lc.getEmissionWave(); RString name; if (value != null) { return rstring(getNameFromWavelength(value)); } Iterator<Filter> i; List<Filter> filters = channelData.getLightPathEmissionFilters(); if (filters != null) { i = filters.iterator(); while (i.hasNext()) { name = getNameFromFilter(i.next()); if (name != null) return name; } } name = getNameFromFilter(channelData.getFilterSetEmissionFilter()); if (name != null) { return name; } //Laser LightSource ls = channelData.getLightSource(); if (ls != null) { if (ls instanceof Laser) { Laser laser = (Laser) ls; value = laser.getWavelength(); if (value != null) { return rstring(getNameFromWavelength(value)); } } } value = lc.getExcitationWave(); if (value != null) { return rstring(getNameFromWavelength(value)); } filters = channelData.getLightPathExcitationFilters(); if (filters != null) { i = filters.iterator(); while (i.hasNext()) { name = getNameFromFilter(i.next()); if (name != null) return name; } } return getNameFromFilter(channelData.getFilterSetExcitationFilter()); } /** * Processes the OMERO client side metadata store. * @param store OMERO metadata store to process. * @throws ModelException If there is an error during processing. */ public void process(IObjectContainerStore store) throws ModelException { this.store = store; reader = this.store.getReader(); if (reader == null) { log.warn("Unexpected null reader."); return; } List<Image> images = store.getSourceObjects(Image.class); String[] domains = reader.getDomains(); boolean isGraphicsDomain = false; for (String domain : domains) { if (FormatTools.GRAPHICS_DOMAIN.equals(domain)) { log.debug("Images are of the graphics domain."); isGraphicsDomain = true; break; } } log.debug("isGraphicsDomain: "+isGraphicsDomain); int count; boolean v; Map<ChannelData, Boolean> m; Pixels pixels; int sizeC; ChannelData channelData; Iterator<ChannelData> k; for (int i = 0; i < images.size(); i++) { pixels = (Pixels) store.getSourceObject(new LSID(Pixels.class, i)); if (pixels == null) { throw new ModelException("Unable to locate Pixels:" + i); } //Require to reset transmitted light count = 0; m = new HashMap<ChannelData, Boolean>(); //Think of strategy for images with high number of channels //i.e. > 6 sizeC = pixels.getSizeC().getValue(); if (sizeC == 1) { channelData = ChannelData.fromObjectContainerStore(store, i, 0); setSingleChannel(channelData); } else { for (int c = 0; c < sizeC; c++) { channelData = ChannelData.fromObjectContainerStore(store, i, c); //Color section populateDefault(channelData, isGraphicsDomain); //only retrieve if not graphics if (!isGraphicsDomain) { //Determine if the channel same emission wavelength. v = ColorsFactory.hasEmissionData(channelData); if (!v) { count++; } m.put(channelData, v); } } } //Need to reset the color of transmitted light //i.e. images with several "emission channels" //check if 0 size if (count > 0 && count != m.size()) { k = m.keySet().iterator(); while (k.hasNext()) { channelData = k.next(); if (!m.get(channelData)) { int[] defaultColor = ColorsFactory.newWhiteColor(); Channel channel = channelData.getChannel(); channel.setRed(rint(defaultColor[ColorsFactory.RED_INDEX])); channel.setGreen(rint(defaultColor[ColorsFactory.GREEN_INDEX])); channel.setBlue(rint(defaultColor[ColorsFactory.BLUE_INDEX])); channel.setAlpha(rint(defaultColor[ColorsFactory.ALPHA_INDEX])); } } } } } }