/* * Copyright 2010-2015 Institut Pasteur. * * This file is part of Icy. * * Icy 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 3 of the License, or * (at your option) any later version. * * Icy 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 Icy. If not, see <http://www.gnu.org/licenses/>. */ package icy.math; import icy.type.TypeUtil; import icy.type.collection.array.ArrayUtil; import java.util.Arrays; /** * @author Stephane */ public class Histogram { protected final int[] bins; protected final double minValue; protected final double maxValue; protected final boolean integer; protected final double dataToBin; protected final double binWidth; /** * Create a histogram for the specified value range and the desired number of bins. * * @param minValue * minimum value * @param maxValue * maximum value * @param nbBin * number of desired bins (should be > 0). * @param integer * If true the input value are considered as integer values.<br> * Bins number is then clamped to values range to not bias the histogram.<br> * In this case the effective number of bins can differ from the desired one. */ public Histogram(double minValue, double maxValue, int nbBin, boolean integer) { super(); this.minValue = minValue; this.maxValue = maxValue; this.integer = integer; final double range = (maxValue - minValue); double bw; if (integer) { if (nbBin > range) bw = 1d; else { bw = (range + 1d) / nbBin; bw = Math.max(1d, Math.floor(bw)); } bins = new int[(int) Math.ceil((range + 1d) / bw)]; } else { bw = range / nbBin; bins = new int[nbBin]; } binWidth = bw; // data to bin index conversion ratio if (range > 0) dataToBin = (bins.length - 1) / range; else dataToBin = 0d; } /** * Reset histogram */ public void reset() { Arrays.fill(bins, 0); } /** * Add the value to the histogram */ public void addValue(double value) { final int index = (int) ((value - minValue) * dataToBin); if ((index >= 0) && (index < bins.length)) bins[index]++; } /** * Add the specified array of values to the histogram * * @param signed * false if the input array should be interpreted as unsigned values<br> * (integer type only) */ public void addValues(Object array, boolean signed) { switch (ArrayUtil.getDataType(array)) { case BYTE: addValues((byte[]) array, signed); break; case SHORT: addValues((short[]) array, signed); break; case INT: addValues((int[]) array, signed); break; case LONG: addValues((long[]) array, signed); break; case FLOAT: addValues((float[]) array); break; case DOUBLE: addValues((double[]) array); break; } } /** * Add the specified byte array to the histogram */ public void addValues(byte[] array, boolean signed) { if (signed) { for (byte value : array) bins[(int) ((value - minValue) * dataToBin)]++; } else { for (byte value : array) bins[(int) ((TypeUtil.unsign(value) - minValue) * dataToBin)]++; } } /** * Add the specified short array to the histogram */ public void addValues(short[] array, boolean signed) { if (signed) { for (short value : array) bins[(int) ((value - minValue) * dataToBin)]++; } else { for (short value : array) bins[(int) ((TypeUtil.unsign(value) - minValue) * dataToBin)]++; } } /** * Add the specified int array to the histogram */ public void addValues(int[] array, boolean signed) { if (signed) { for (int value : array) bins[(int) ((value - minValue) * dataToBin)]++; } else { for (int value : array) bins[(int) ((TypeUtil.unsign(value) - minValue) * dataToBin)]++; } } /** * Add the specified long array to the histogram */ public void addValues(long[] array, boolean signed) { if (signed) { for (long value : array) bins[(int) ((value - minValue) * dataToBin)]++; } else { for (long value : array) bins[(int) ((TypeUtil.unsign(value) - minValue) * dataToBin)]++; } } /** * Add the specified float array to the histogram */ public void addValues(float[] array) { for (float value : array) bins[(int) ((value - minValue) * dataToBin)]++; } /** * Add the specified double array to the histogram */ public void addValues(double[] array) { for (double value : array) bins[(int) ((value - minValue) * dataToBin)]++; } /** * Get bin index from data value */ protected int toBinIndex(double value) { return (int) Math.round((value - minValue) * dataToBin); } /** * Returns the minimum allowed value of the histogram. */ public double getMinValue() { return minValue; } /** * Returns the maximum allowed value of the histogram. */ public double getMaxValue() { return maxValue; } /** * Returns true if the input value are integer values only.<br> * This is used to adapt the bin number. */ public boolean isIntegerType() { return integer; } /** * Returns the number of bins of the histogram. */ public int getBinNumber() { return bins.length; } /** * Return the width of a bin */ public double getBinWidth() { return binWidth; } /** * Returns the size of the specified bin (number of element in the bin) */ public int getBinSize(int index) { if ((index < 0) || (index >= bins.length)) return 0; return bins[index]; } /** * Returns bins of histogram */ public int[] getBins() { return bins; } }