Java tutorial
/* * omeis.providers.re.metadata.StatsFactory * * Copyright 2006 University of Dundee. All rights reserved. * Use is subject to license terms supplied in LICENSE.txt */ package omeis.providers.re.metadata; // Java imports // Third-party libraries // Application-internal dependencies import java.awt.Dimension; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import ome.conditions.ResourceError; import ome.io.nio.PixelBuffer; import ome.io.nio.TileLoopIteration; import ome.io.nio.Utils; import ome.model.core.Channel; import ome.model.core.Pixels; import ome.model.stats.StatsInfo; import omeis.providers.re.Renderer; import omeis.providers.re.data.Plane2D; import omeis.providers.re.data.PlaneDef; import omeis.providers.re.data.PlaneFactory; import omeis.providers.re.data.RegionDef; import omeis.providers.re.quantum.QuantumStrategy; /** * Computes two types of statistics: The PixelsStats and the location stats. The * location stats determine the location of the pixels' values in order to set * the inputWindow and the noiseReduction flag. This flag will then be used when * we map the pixels intensity values onto the device space. * * @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:11:46 $) </small> * @since OME2.2 */ public class StatsFactory { /** The logger for this particular class */ private static Log log = LogFactory.getLog(StatsFactory.class); /** The minimum range. */ private static final int RANGE_RGB = 255; /** The number of bins. */ private static final int NB_BIN = 2 * QuantumStrategy.DECILE; /** The default size of a bin. */ private static final int BIN = 2; /** The error factor. */ private static final int EPSILON = 4; /** The threshold value. */ private static final double THRESHOLD = 0.99; /** The noise reduction threshold value. */ private static final double NR_THRESHOLD = 0.95; /** The location statistics. */ private double[] locationStats; /** Boolean flag to determine the mapping algorithm. */ private boolean noiseReduction; /** Value determined according to the location of the pixels' value. */ private double inputStart; /** Value determined according to the location of the pixels' value. */ private double inputEnd; /** The size of the bin.*/ private double sizeBin; /** The epsilon value.*/ private double epsilon; /** * For the specified {@link Plane2D}, computes the bins, determines the * inputWindow and the noiseReduction flag. * * @param p2D The selected plane2d. * @param stats The statistics for the selected channel. * @param sizeX2 The size along one axis. * @param sizeX1 The size along the other axis. */ private void computeBins(Plane2D p2D, StatsInfo stats, int sizeX2, int sizeX1) { double gMin = stats.getGlobalMin().doubleValue(); int[] totals = new int[NB_BIN]; /* * Segment[] segments = new Segment[NB_BIN]; for (int i = 0; i < NB_BIN; * i++) { segments[i] = new Segment( gMin + i * sizeBin, 0, gMin + (i + * 1) * sizeBin, 0); } */ BasicSegment[] segments = new BasicSegment[NB_BIN]; for (int i = 0; i < NB_BIN; i++) { segments[i] = new BasicSegment(gMin + i * sizeBin, gMin + (i + 1) * sizeBin); } // check segment [o,e[ double v; BasicSegment segment; if (p2D.isXYPlanar()) { // modified code int size = sizeX1 * sizeX2; for (int j = 0; j < size; j++) { v = p2D.getPixelValue(j); for (int i = 0; i < segments.length; i++) { segment = segments[i]; if (v >= segment.x1 && v < segment.x2) { totals[i]++; break; } } // end i } } else { for (int x2 = 0; x2 < sizeX2; ++x2) { for (int x1 = 0; x1 < sizeX1; ++x1) { v = p2D.getPixelValue(x1, x2); for (int i = 0; i < segments.length; i++) { segment = segments[i]; if (v >= segment.x1 && v < segment.x2) { totals[i]++; break; } /* * if (!segments[i].equals(1, pointX1, pointX2) && * segments[i].lies(pointX1, pointX2)) { totals[i]++; * break; } */ } // end i } // end x1 } // end x2 } double total = sizeX2 * sizeX1; for (int i = 0; i < totals.length; i++) { locationStats[i] += totals[i] / total; } // Default, we assume that we have at least 3 sub-intervals. /* inputStart = segments[0].x2;// segments[0].getPoint(1).x1; inputEnd = segments[NB_BIN - 1].x2;// segments[NB_BIN - // 1].getPoint(1).x1; total = total - totals[0] - totals[NB_BIN - 1]; if (totals[0] >= totals[NB_BIN - 1]) { inputEnd = accumulateCloseToMin(totals, segments, total, epsilon); } else { inputStart = accumulateCloseToMax(totals, segments, total, epsilon); } */ double s = segments[0].x2; double end = segments[NB_BIN - 1].x2; total = total - totals[0] - totals[NB_BIN - 1]; if (totals[0] >= totals[NB_BIN - 1]) { end = accumulateCloseToMin(totals, segments, total, epsilon); } else { s = accumulateCloseToMax(totals, segments, total, epsilon); } if (s < inputStart) inputStart = s; if (end > inputEnd) inputEnd = end; noiseReduction = noiseReduction(); } /** Determines the value of the noiseReduction flag. */ private boolean noiseReduction() { double sumMin = 0, sumMax = 0; for (int i = 0; i < locationStats.length; i++) { if (i < BIN) { sumMin += locationStats[i]; } if (i >= locationStats.length - BIN) { sumMax += locationStats[i]; } } if (sumMin >= NR_THRESHOLD || sumMax >= NR_THRESHOLD) { return false; } return true; } /** * Determines the value of inputEnd when the pixels' values accumulated * closed to the minimum. * * @param totals The accumulated values. * @param segments The segments to analyze. * @param total The total value. * @param epsilon The error value. */ private double accumulateCloseToMin(int[] totals, BasicSegment[] segments, double total, double epsilon) { double e = segments[NB_BIN - 1].x2, sum = 0; for (int i = 1; i < totals.length - 1; i++) { sum += totals[i]; if (sum / total > THRESHOLD) { e = segments[i].x1 + epsilon; break; } } return e; } /** * Determines the value of inputStart when the pixels' values accumulated * closed to the max. * * @param totals The accumulated values. * @param segments The segments to analyze. * @param total The total value. * @param epsilon The error value. */ private double accumulateCloseToMax(int[] totals, BasicSegment[] segments, double total, double epsilon) { double s = segments[0].x2, sum = 0; for (int i = totals.length - 2; i > 0; i--) { sum += totals[i]; if (sum / total > THRESHOLD) { s = segments[i].x2 - epsilon; break; } } return s; } /** * Helper object to determine the location of the pixels' values, the * inputWindow i.e. <code>inputStart</code> and <code>inputEnd</code> * and to initialize the <code>noiseReduction</code> flag. * * @param metadata The pixels to parse. * @param pixelsData The buffer. * @param pd The plane to handle. * @param index The channel index. * @throws PixMetadataException */ public void computeLocationStats(final Pixels metadata, final PixelBuffer pixelsData, final PlaneDef pd, final int index) { log.debug("Computing location stats for Pixels:" + metadata.getId()); Channel channel = metadata.getChannel(index); final StatsInfo stats = channel.getStatsInfo(); if (stats == null) { throw new ResourceError("Pixels set is missing statistics for " + "channel '" + index + "'. This suggests an image import " + "error or failed image import."); } double gMin = stats.getGlobalMin().doubleValue(); double gMax = stats.getGlobalMax().doubleValue(); Dimension tileSize = pixelsData.getTileSize(); double range = gMax - gMin; if (range <= RANGE_RGB) { inputEnd = gMax; inputStart = gMin; return; } sizeBin = range / NB_BIN; epsilon = sizeBin / EPSILON; if (locationStats == null) { locationStats = new double[NB_BIN]; } //value will be reset when calculating data. inputStart = gMax; inputEnd = gMin; Utils.forEachTile(new TileLoopIteration() { public void run(int z, int c, int t, int x, int y, int tileWidth, int tileHeight, int tileCount) { if (z == 1 || c == 1 || t == 1) { // We're not going through the entire pixel buffer return; } RegionDef regionDef = new RegionDef(); regionDef.setX(x); regionDef.setY(y); regionDef.setWidth(tileWidth); regionDef.setHeight(tileHeight); pd.setRegion(regionDef); Plane2D plane2D = PlaneFactory.createPlane(pd, index, metadata, pixelsData); computeBins(plane2D, stats, tileHeight, tileWidth); } }, pixelsData, (int) tileSize.getWidth(), (int) tileSize.getHeight()); } /** * Returns the statistics. * * @return See above. */ public double[] getLocationStats() { return locationStats; } /** * Returns <code>true</code> if the flag is on, <code>false</code> * otherwise. * * @return See above. */ public boolean isNoiseReduction() { return noiseReduction; } /** * Returns the input start. * * @return See above. */ public double getInputStart() { return inputStart; } /** * Returns the input end. * * @return See above. */ public double getInputEnd() { return inputEnd; } // inner class class BasicSegment { /** Left bound of the segment. */ double x1; /** Right bound of the segment. */ double x2; /** * Creates a new instance. * * @param x1 * The left bound of the segment. * @param x2 * The right bound of the segment. */ BasicSegment(double x1, double x2) { if (x2 < x1) { throw new IllegalArgumentException("Segment not valid."); } this.x2 = x2; this.x1 = x1; } } }