/* * omeis.providers.re.quantum.QuantumStrategy * * Copyright 2006-2015 University of Dundee. All rights reserved. * Use is subject to license terms supplied in LICENSE.txt */ package omeis.providers.re.quantum; import ome.model.core.Pixels; import ome.model.display.QuantumDef; import ome.model.enums.Family; import omeis.providers.re.data.PlaneFactory; import omeis.providers.re.metadata.StatsFactory; /** * Subclasses Work on explicit pixel types. Taking into * account the pixel types, transform the pixel intensity value passed to * {@link #quantize} by delegating to the configured quantum map. Encapsulate a * computation strategy for the quantization process i.e. LUT and Approximation. * Implement {@link #onWindowChange} to get notified when the input interval * changes. * * @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> * @version 2.2 <small> (<b>Internal version:</b> $Revision$ $Date: * 2005/06/20 14:12:05 $) </small> * @since OME2.2 */ public abstract class QuantumStrategy { /** * Maximum value (<code>255</code>) allowed for the upper bound of the * codomain interval. */ public static final int MAX = 255; /** * Minimum value (<code>0</code>) allowed for the lower bound of the * codomain interval. */ public static final int MIN = 0; /** * Determines the number of sub-intervals of the [globalMin, globalMax] * interval. */ public static final int DECILE = 10; /** The maximum size for a lookup table. */ static final double MAX_SIZE_LUT = 0x10000; /** The maximum size of the cache.*/ static final long MAX_SIZE = 1000; /** The minimum value for the pixels type. */ private double pixelsTypeMin; /** The maximum value for the pixels type. */ private double pixelsTypeMax; /** Minimum of all minima. */ private double globalMin; /** Maximum of all maxima. */ private double globalMax; /** The original Minimum of all minima. */ private double originalGlobalMin; /** The original Maximum of all maxima. */ private double originalGlobalMax; /** The lower limit of the input Interval i.e. pixel intensity interval. */ private double windowStart; /** The upper limit of the input Interval i.e. pixel intensity interval. */ private double windowEnd; /** Identifies a family of maps. */ private Family family; /** Selects a curve in the family. */ private double curveCoefficient; /** * Identifies the noise reduction algorithm, value set to <code>true</code> * if the noise reduction algorithm is applied, <code>false</code> * otherwise. */ private boolean noiseReduction; /** Reference to a quantumDef object. */ protected final QuantumDef qDef; /** The pixels this strategy is for. */ protected final Pixels pixels; /** Reference to the value mapper. */ protected QuantumMap valueMapper; /** * Defines the value mapper corresponding to the specified family. * * @param family * The family identifying the value mapper. */ private void defineMapper(Family family) { String value = family.getValue(); verifyFamily(value); if (value.equals(QuantumFactory.LINEAR) || value.equals(QuantumFactory.POLYNOMIAL)) { valueMapper = new PolynomialMap(); } else if (value.equals(QuantumFactory.LOGARITHMIC)) { valueMapper = new LogarithmicMap(); } else if (value.equals(QuantumFactory.EXPONENTIAL)) { valueMapper = new ExponentialMap(); } } /** * Controls if the specified family is supported. The family must be one of * the constant defined in {@link QuantumFactory}. * * @param value * The family we're checking for validity. */ private static void verifyFamily(String value) { if (!value.equals(QuantumFactory.LINEAR) && !value.equals(QuantumFactory.LOGARITHMIC) && !value.equals(QuantumFactory.EXPONENTIAL) && !value.equals(QuantumFactory.POLYNOMIAL)) { throw new IllegalArgumentException("Unsupported family type: '" + value + "'"); } } /** * Controls if the specified interval is valid depending on the pixel type. * * The min value and max value could be out of pixel type range b/c of an * error occurred during the calculations of the statistics. * Returns <code>true</code> if the interval is valid according to the * pixels type, <code>false</code> otherwise. * * @param min * The lower bound of the interval. * @param max * The upper bound of the interval. * @return See above. */ private boolean verifyInterval(double min, double max) { boolean b = false; if (min <= max) { double range = max - min; if (PlaneFactory.in(pixels.getPixelsType(), new String[] { PlaneFactory.INT8, PlaneFactory.UINT8 })) { if (range < 0x100) { b = true; } } else if (PlaneFactory .in(pixels.getPixelsType(), new String[] { PlaneFactory.INT16, PlaneFactory.UINT16 })) { if (range < 0x10000) { b = true; } } else if (PlaneFactory .in(pixels.getPixelsType(), new String[] { PlaneFactory.INT32, PlaneFactory.UINT32 })) { if (range < 0x100000000L) { b = true; } } else if (PlaneFactory .in(pixels.getPixelsType(), new String[] { PlaneFactory.FLOAT_TYPE, PlaneFactory.DOUBLE_TYPE })) { b = true; } } return b; } /** * Initializes the minimum and maximum used to build a LUT depending on the * pixels type. * * @param withRange Pass <code>true</code> to indicate that the range * has to be taken into account, <code>false</code> * otherwise. */ private void initPixelsRange(boolean withRange) { StatsFactory sf = new StatsFactory(); double[] values = sf.initPixelsRange(pixels); pixelsTypeMin = values[0]; pixelsTypeMax = values[1]; } /** * Creates a new instance. * * @param qd The {@link QuantumDef} this strategy is for. * @param pixels The pixels to handle. */ protected QuantumStrategy(QuantumDef qd, Pixels pixels) { windowStart = globalMin = 0.0; windowEnd = globalMax = 1.0; curveCoefficient = 1.0; pixelsTypeMax = 0.0; pixelsTypeMin = 0.0; if (qd == null) { throw new NullPointerException("No quantum definition"); } this.qDef = qd; if (pixels == null) { throw new NullPointerException("No pixels"); } this.pixels = pixels; initPixelsRange(false); } /** * Returns the range the values belongs to. * * @param value The value to handle * @return See above. */ protected Double getMiddleRange(double value) { //no range so we need to create it double min = getWindowStart(); double max = getWindowEnd(); if (value < min) return min; if (value > max) return max; double step = Math.abs(max-min)/(qDef.getCdEnd()-qDef.getCdStart()+1); if (value == min) { return min+step/2; } int v = (int) ((value-min)/step); return (min+(v-1)*step+min+v*step)/2; } /** * Sets the maximum range of the input window. * * @param globalMin * The minimum of all minima for a specified stack. * @param globalMax * The maximum of all maxima for a specified stack. */ public void setExtent(double globalMin, double globalMax) { originalGlobalMin = globalMin; originalGlobalMax = globalMax; initPixelsRange(false); if (Double.isInfinite(globalMax) || globalMax > pixelsTypeMax) globalMax = pixelsTypeMax; if (Double.isInfinite(globalMin) || globalMin < pixelsTypeMin) globalMin = pixelsTypeMin; //Check if outside the pixel range so we set to the pixels type if (!verifyInterval(globalMin, globalMax)) { globalMax = pixelsTypeMax; globalMin = pixelsTypeMin; } this.globalMin = globalMin; this.globalMax = globalMax; this.windowStart = globalMin; this.windowEnd = globalMax; initPixelsRange(true); } /** * Sets the input window interval. * * @param start * The lower bound of the interval. * @param end * The upper bound of the interval. */ public void setWindow(double start, double end) { verifyInterval(start, end); //Check if outside the pixel range so we set to the pixels type if (!verifyInterval(start, end)) { end = pixelsTypeMax; start = pixelsTypeMin; } if (start < pixelsTypeMin) start = pixelsTypeMin; if (end > pixelsTypeMax) end = pixelsTypeMax; windowStart = start; windowEnd = end; onWindowChange(); } /** * Sets the selected family, the curve coefficient and the noise reduction * flag. * * @param family * The mapping family. * @param k * The curve coefficient. * @param noiseReduction * The noise reduction flag. */ public void setMapping(Family family, double k, boolean noiseReduction) { defineMapper(family); this.family = family; curveCoefficient = k; this.noiseReduction = noiseReduction; } /** * Sets the selected family, the curve coefficient and the noise reduction * flag and rebuilds the look-up table. * * @param family * The mapping family. * @param k * The curve coefficient. * @param noiseReduction * The noise reduction flag. */ public void setQuantizationMap(Family family, double k, boolean noiseReduction) { setMapping(family, k, noiseReduction); onWindowChange(); } /** * Sets the quantum map. * * @param qMap The value to set. */ public void setMap(QuantumMap qMap) { valueMapper = qMap; } /** * Returns the mapping family. * * @return See above. */ public Family getFamily() { return family; } /** * Returns the coefficient identifying a curve within a given family. * * @return See above. */ public double getCurveCoefficient() { return curveCoefficient; } /** * Returns <code>true</code> if the noise reduction algorithm * is turned on, <code>false</code> if turned off. * * @return See above. */ public boolean getNoiseReduction() { return noiseReduction; } /** * Returns the minimum of all minima. * * @return See above. */ public double getGlobalMin() { // needed b/c of float value double d = globalMin - Math.floor(globalMin); if (d != 0) { globalMin = Math.floor(globalMin); } return globalMin; } /** * Returns the maximum of all maxima. * * @return See above. */ public double getGlobalMax() { return globalMax; } /** * Returns the original minimum of all minima. * * @return See above. */ public double getOriginalGlobalMin() { return originalGlobalMin; } /** * Returns the original maximum of all minima. * * @return See above. */ public double getOriginalGlobalMax() { return originalGlobalMax; } /** * Returns the lower bound of the pixels range or <code>0</code> * if the value couldn't be set. * * @return See above. */ public double getPixelsTypeMin() { return pixelsTypeMin; } /** * Returns the upper bound of the pixels range or <code>0</code> * if the value couldn't be set. * * @return See above. */ public double getPixelsTypeMax() { return pixelsTypeMax; } /** * Returns the lower bound of the input interval. * * @return See above. */ public double getWindowStart() { return windowStart; } /** * Returns the upper bound of the input interval. * * @return See above. */ public double getWindowEnd() { return windowEnd; } /** * Notifies when the input interval has changed or the mapping strategy has * changed. */ protected abstract void onWindowChange(); /** * Maps a value from [windowStart, windowEnd] to a value in the codomain * interval. * * @param value * The pixel intensity value. * @return The value in the codomain interval i.e. sub-interval of [0, * 255]. * @throws QuantizationException * If the specified value is not in the interval [globalMin, * globalMax]. */ public abstract int quantize(double value) throws QuantizationException; }