/*
* org.openmicroscopy.shoola.env.rnd.roi.ROIStats
*
*------------------------------------------------------------------------------
* Copyright (C) 2006-2015 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 org.openmicroscopy.shoola.env.rnd.roi;
import java.awt.Point;
import java.util.HashMap;
import java.util.Map;
import omero.model.Pixels;
/**
* collects statistics for a given roi
*
* @author Jean-Marie Burel
* <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
* @author Donald MacDonald
* <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a>
* @version 3.0
* @since OME3.0
*/
public class ROIStats
implements PointIteratorObserver
{
/**
* The dimensions of the pixels set over which the statistic will be
* computed.
*/
private Pixels dims;
/**
* Maps a {@link #linearize(int, int, int) linearized} <code>(z, w, t)
* </code> tuple identifying a plane onto the statistics calculated for the
* 2D-selection contained in that plane.
*/
private Map<Integer, ROIShapeStats> arrayMap;
/**
* Transforms 3D coordinates into linear coordinates.
* The returned value <code>L</code> is calculated as follows:
* <code>L = sizeZ*sizeW*t + sizeZ*w + z</code>.
*
* @param z The z coordinate. Must be in the range <code>[0, sizeZ)</code>.
* @param w The w coordinate. Must be in the range <code>[0, sizeW)</code>.
* @param t The t coordinate. Must be in the range <code>[0, sizeT)</code>.
* @return The linearized value corresponding to <code>(z, w, t)</code>.
*/
private Integer linearize(int z, int w, int t)
{
int sizeZ = dims.getSizeZ().getValue();
int sizeC = dims.getSizeC().getValue();
if (z < 0 || sizeZ <= z)
throw new IllegalArgumentException(
"z out of range [0, "+sizeZ+"): "+z+".");
if (w < 0 || sizeC <= w)
throw new IllegalArgumentException(
"w out of range [0, "+sizeC+"): "+w+".");
if (t < 0 || dims.getSizeT().getValue() <= t)
throw new IllegalArgumentException(
"t out of range [0, "+dims.getSizeT()+"): "+t+".");
return Integer.valueOf(sizeZ*sizeC*t + sizeZ*w + z);
}
/**
* Creates a new object to collect statistics for a given ROI.
*
* @param dims The dimensions of the pixels set over which the statistics
* will be computed. Mustn't be <code>null</code>.
*/
public ROIStats(Pixels dims)
{
if (dims == null) throw new NullPointerException("No dims.");
this.dims = dims;
}
/**
* Returns the statistics, if any, that were calculated against
* the 2D-selection within the specified plane.
*
* @param z The z coordinate. Must be in the range <code>[0, sizeZ)</code>.
* @param w The w coordinate. Must be in the range <code>[0, sizeW)</code>.
* @param t The t coordinate. Must be in the range <code>[0, sizeT)</code>.
* @return A {@link ROIShapeStats} object holding the statistics for the
* 2D-selection in the specified plane. If no selection was
* made in that plane, then <code>null</code> is returned instead.
*/
public AbstractROIShapeStats getPlaneStats(int z, int w, int t)
{
Integer index = linearize(z, w, t);
return arrayMap.get(index);
}
/**
* Creates a new map to store the {@link ROIShapeStats} entries that are
* about to be calculated.
* @see PointIteratorObserver#iterationStarted()
*/
public void iterationStarted()
{
arrayMap = new HashMap<Integer, ROIShapeStats>();
}
/**
* Creates a new {@link ROIShapeStats} entry for the plane selection that
* is about to be iterated.
* @see PointIteratorObserver#onStartPlane(int, int, int, int)
*/
public void onStartPlane(int z, int w, int t, int pointsCount)
{
ROIShapeStats planeStats = new ROIShapeStats();
Integer index = linearize(z, w, t);
arrayMap.put(index, planeStats);
}
/**
* Updates the min, max, and sum values of the current {@link ROIShapeStats}
* entry as needed.
* @see PointIteratorObserver#update(double, int, int, int, Point)
*/
public void update(double pixelValue, int z, int w, int t, Point loc)
{
Integer index = linearize(z, w, t);
AbstractROIShapeStats planeStats = arrayMap.get(index);
//planeStats can't be null, see onStartPlane().
if (pixelValue < planeStats.getMin())
planeStats.setMin(pixelValue);
if (planeStats.getMax() < pixelValue)
planeStats.setMax(pixelValue);
planeStats.addToSum(pixelValue);
planeStats.addToSumOfSquares(pixelValue);
}
/**
* Calculates the mean and standard deviation for the current
* {@link ROIShapeStats} entry.
* @see PointIteratorObserver#onEndPlane(int, int, int, int)
*/
public void onEndPlane(int z, int w, int t, int pointsCount)
{
Integer index = linearize(z, w, t);
AbstractROIShapeStats ps = arrayMap.get(index);
//planeStats can't be null, see onStartPlane().
if (0 < pointsCount) {
ps.setMean(ps.getSum()/pointsCount);
ps.setPointsCount(pointsCount);
if (0 < pointsCount-1) {
double sigmaSquare =
(ps.getSumOfSquares() - ps.getSum()*ps.getSum()/pointsCount)
/(pointsCount-1);
if (sigmaSquare > 0)
ps.setStandardDeviation(Math.sqrt(sigmaSquare));
}
}
}
/**
* Required by {@link PointIteratorObserver} interface, but no-operation
* implementation in our case.
* @see PointIteratorObserver#iterationFinished()
*/
public void iterationFinished() {}
}