All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.imglib2.histogram.HistogramNd Maven / Gradle / Ivy

There is a newer version: 7.1.4
Show newest version
/*
 * #%L
 * ImgLib2: a general-purpose, multidimensional image processing library.
 * %%
 * Copyright (C) 2009 - 2018 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld,
 * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke,
 * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner,
 * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert,
 * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin,
 * Jean-Yves Tinevez and Michael Zinsmaier.
 * %%
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * #L%
 */

package net.imglib2.histogram;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import net.imglib2.Cursor;
import net.imglib2.Interval;
import net.imglib2.Positionable;
import net.imglib2.RandomAccess;
import net.imglib2.RealPositionable;
import net.imglib2.img.Img;
import net.imglib2.img.ImgFactory;
import net.imglib2.type.numeric.integer.LongType;

// TODO - calculate lazily but should be able to count upper/lower/middle in
// one pass rather than the multiple passes that are now in place.

/**
 * A HistogramNd is an n-dimensional histogram that tracks up to four kinds of
 * values: 1) values in the center of the distribution 2) values to the left of
 * the center of the distribution (lower tail) 3) values to the right of the
 * center of the distribution (upper tail) 4) values outside the other areas
 * 

* Note: the last three classifications may not be present depending upon the * makeup of the input data. * * @author Barry DeZonia */ public class HistogramNd< T > implements Img< LongType > { // -- instance variables -- private List< BinMapper1d< T >> mappers; private DiscreteFrequencyDistribution distrib; private long[] pos; private long ignoredCount; @SuppressWarnings( "synthetic-access" ) private final Incrementer incrementer = new Incrementer(); @SuppressWarnings( "synthetic-access" ) private final Decrementer decrementer = new Decrementer(); // -- constructors -- /** * Construct a histogram from a list of bin mapping algorithms. Use * countData() to populate it. * * @param mappers * The algorithms used to map values to bins */ public HistogramNd( final List< BinMapper1d< T >> mappers ) { this.mappers = mappers; final long[] dims = new long[ mappers.size() ]; for ( int i = 0; i < mappers.size(); i++ ) { dims[ i ] = mappers.get( i ).getBinCount(); } distrib = new DiscreteFrequencyDistribution( dims ); pos = new long[ mappers.size() ]; ignoredCount = 0; } /** * Construct a histogram whose bin mappings match another histogram. After * this construction the histogram bins are unpopulated. * * @param other * The histogram to copy. */ public HistogramNd( final HistogramNd< T > other ) { final List< BinMapper1d< T >> mappersCopy = new ArrayList< BinMapper1d< T >>(); for ( final BinMapper1d< T > m : mappers ) { mappersCopy.add( m.copy() ); } mappers = mappersCopy; distrib = other.distrib.copy(); pos = other.pos.clone(); ignoredCount = 0; } /** * Construct a histogram from an iterable set of data and a list of bin * mapping algorithms. Must be given one iterable data source that returns * multiple data values at each point. * * @param data * The iterable set of values to calculate upon * @param mappers * The algorithms used to map values to bins */ public HistogramNd( final Iterable< List< T >> data, final List< BinMapper1d< T >> mappers ) { this( mappers ); init( data ); } /** * Construct a histogram from an iterable set of data and a list of bin * mapping algorithms. Must be given multiple iterable data sources that * each return a single data value at each point. * * @param data * The iterable set of values to calculate upon * @param mappers * The algorithms used to map values to bins */ public HistogramNd( final List< Iterable< T >> data, final List< BinMapper1d< T >> mappers ) { this( mappers ); init( data ); } // -- public api -- /** * Returns true if the histogram has tail bins which count extreme values * for the given dimension. */ public boolean hasTails( final int dim ) { return mappers.get( dim ).hasTails(); } /** * Returns true if the histogram has tail bins which count extreme values * for one or more dimensions */ public boolean hasTails() { for ( int i = 0; i < mappers.size(); i++ ) { if ( hasTails( i ) ) return true; } return false; } /** * Returns the frequency count of values in the lower tail bin (if any) for * the given dimension. */ public long lowerTailCount( final int dim ) { if ( !hasTails( dim ) ) return 0; long sum = 0; final Cursor< ? > cursor = distrib.localizingCursor(); final long[] binPos = new long[ distrib.numDimensions() ]; while ( cursor.hasNext() ) { cursor.next(); cursor.localize( binPos ); if ( binPos[ dim ] == 0 ) sum += distrib.frequency( binPos ); } return sum; } /** * Returns the frequency count of values in all lower tail bins (if any). */ public long lowerTailCount() { if ( !hasTails() ) return 0; long sum = 0; final Cursor< ? > cursor = distrib.localizingCursor(); final long[] binPos = new long[ distrib.numDimensions() ]; while ( cursor.hasNext() ) { cursor.next(); cursor.localize( binPos ); for ( int i = 0; i < mappers.size(); i++ ) { if ( binPos[ i ] == 0 ) { sum += distrib.frequency( binPos ); break; } } } return sum; } /** * Returns the frequency count of values in the upper tail bin (if any) for * the given dimension. */ public long upperTailCount( final int dim ) { if ( !hasTails( dim ) ) return 0; final long dimSize = mappers.get( dim ).getBinCount(); long sum = 0; final Cursor< ? > cursor = distrib.localizingCursor(); final long[] binPos = new long[ distrib.numDimensions() ]; while ( cursor.hasNext() ) { cursor.next(); cursor.localize( binPos ); if ( binPos[ dim ] == dimSize - 1 ) sum += distrib.frequency( binPos ); } return sum; } /** * Returns the frequency count of values in all upper tail bins (if any). */ public long upperTailCount() { if ( !hasTails() ) return 0; long sum = 0; final Cursor< ? > cursor = distrib.localizingCursor(); final long[] binPos = new long[ distrib.numDimensions() ]; while ( cursor.hasNext() ) { cursor.next(); cursor.localize( binPos ); for ( int i = 0; i < mappers.size(); i++ ) { if ( binPos[ i ] == mappers.get( i ).getBinCount() - 1 ) { sum += distrib.frequency( binPos ); break; } } } return sum; } /** * Returns the frequency count of all values in the middle of the * distribution for a given dimension. */ public long valueCount( final int dim ) { final boolean hasTails = hasTails( dim ); final long dimSize = mappers.get( dim ).getBinCount(); long sum = 0; final Cursor< ? > cursor = distrib.localizingCursor(); final long[] binPos = new long[ distrib.numDimensions() ]; while ( cursor.hasNext() ) { cursor.next(); cursor.localize( binPos ); boolean inTail = false; if ( hasTails && ( binPos[ dim ] == 0 ) || ( binPos[ dim ] == dimSize - 1 ) ) { inTail = true; } if ( !inTail ) sum += distrib.frequency( binPos ); } return sum; } /** * Returns the frequency count of all values in the middle of the * distribution. */ public long valueCount() { // NB : would like to do this: // return distributionCount() - lowerTailCount() - upperTailCount(); // But this double counts some tail bins. if ( !hasTails() ) return distributionCount(); long sum = 0; final Cursor< ? > cursor = distrib.localizingCursor(); final long[] binPos = new long[ distrib.numDimensions() ]; while ( cursor.hasNext() ) { cursor.next(); cursor.localize( binPos ); boolean inTail = false; for ( int i = 0; i < binPos.length; i++ ) { if ( ( binPos[ i ] == 0 ) || ( binPos[ i ] == mappers.get( i ).getBinCount() - 1 ) ) { inTail = true; break; } } if ( !inTail ) sum += distrib.frequency( binPos ); } return sum; } /** * Returns the frequency count of all values in the specified dimension of * the distribution: lower tail + middle + upper tail. Does not include * ignored values. */ public long distributionCount( final int dim, final long dimVal ) { long sum = 0; final Cursor< ? > cursor = distrib.localizingCursor(); final long[] binPos = new long[ distrib.numDimensions() ]; while ( cursor.hasNext() ) { cursor.next(); cursor.localize( binPos ); if ( binPos[ dim ] != dimVal ) continue; sum += distrib.frequency( binPos ); } return sum; } /** * Returns the frequency count of all values in the distribution: lower tail * + middle + upper tail. Does not include ignored values. */ public long distributionCount() { return distrib.totalValues(); } /** * Returns the frequency count of values that were ignored because they * could not be mapped to any bin. */ public long ignoredCount() { return ignoredCount; } /** * Returns the total count of all values observed; both within and without * the entire distribution. Thus it includes ignored values. One should * decide carefully between using distributionCount() and totalCount(). */ public long totalCount() { return distributionCount() + ignoredCount(); } /** * Returns the frequency count of values within a bin using a set of * representative values. Note that multiple values can be mapped to one bin * so this is NOT the frequency count of this exact set of values in the * distribution. * * @param values * A set of representative values of interest */ public long frequency( final List< T > values ) { map( values, pos ); return frequency( pos ); } /** * Returns the frequency count of the values within a bin. */ public long frequency( final long[] binPos ) { return distrib.frequency( binPos ); } /** * Returns the relative frequency of values within a bin using a set of * representative values. Note that multiple values can be mapped to one bin * so this is NOT the relative frequency of this exact set of values in the * distribution. *

* This calculation is of the number of values in the bin divided by either * the number of values in the distribution or the number of values in the * center of the distribution (tails ignored). *

* One can devise other ways to count relative frequencies that consider * ignored values also. If needed one can use the various count methods and * frequency methods to calculate any relative frequency desired. * * @param values * A representative set of values of interest * @param includeTails * Flag for determining whether to include tails in calculation. */ public double relativeFrequency( final List< T > values, final boolean includeTails ) { map( values, pos ); return relativeFrequency( pos, includeTails ); } /** * Returns the relative frequency of values within a bin. *

* This calculation is of the number of values in the bin divided by either * the number of values in the distribution or the number of values in the * center of the distribution (tails ignored). *

* One can devise other ways to count relative frequencies that consider * ignored values also. If needed one can use the various count methods and * frequency methods to calculate any relative frequency desired. * * @param binPos * The position of the bin of interest * @param includeTails * Flag for determining whether to include tails in calculation. */ public double relativeFrequency( final long[] binPos, final boolean includeTails ) { final double numer = frequency( binPos ); final long denom = includeTails ? totalCount() : valueCount(); return numer / denom; } /** * Returns the number of bins contained in the histogram. */ public long getBinCount() { if ( mappers.size() == 0 ) return 0; long count = 1; for ( int i = 0; i < mappers.size(); i++ ) { count *= mappers.get( i ).getBinCount(); } return count; } /** * Fills a bin position by mapping from a set of representative values. */ public void map( final List< T > values, final long[] binPos ) { for ( int i = 0; i < mappers.size(); i++ ) { binPos[ i ] = mappers.get( i ).map( values.get( i ) ); } } /** * Gets the values associated with the center of a bin. * * @param binPos * The bin index of interest * @param values * The outputs to fill with the center values */ public void getCenterValues( final long[] binPos, final List< T > values ) { for ( int i = 0; i < mappers.size(); i++ ) { final T value = values.get( i ); mappers.get( i ).getCenterValue( binPos[ i ], value ); } } /** * Gets the values associated with the left edge of a bin. * * @param binPos * The bin index of interest * @param values * The outputs to fill with the left edge values */ public void getLowerBounds( final long[] binPos, final List< T > values ) { for ( int i = 0; i < mappers.size(); i++ ) { final T value = values.get( i ); mappers.get( i ).getLowerBound( binPos[ i ], value ); } } /** * Gets the values associated with the right edge of a bin. * * @param binPos * The bin index of interest * @param values * The outputs to fill with the right edge values */ public void getUpperBounds( final long[] binPos, final List< T > values ) { for ( int i = 0; i < mappers.size(); i++ ) { final T value = values.get( i ); mappers.get( i ).getUpperBound( binPos[ i ], value ); } } /** * Returns true if the given bin interval is closed on the right * * @param binPos * The bin number of the interval of interest */ public boolean includesUpperBounds( final long[] binPos ) { for ( int i = 0; i < mappers.size(); i++ ) { if ( !mappers.get( i ).includesUpperBound( binPos[ i ] ) ) return false; } return true; } /** * Returns true if the given bin interval is closed on the left * * @param binPos * The bin number of the interval of interest */ public boolean includesLowerBounds( final long[] binPos ) { for ( int i = 0; i < mappers.size(); i++ ) { if ( !mappers.get( i ).includesLowerBound( binPos[ i ] ) ) return false; } return true; } /** * Returns true if the given bin interval is closed on the right for the * given dimension. * * @param dim * The dimension of interest * @param binPos * The bin number of the interval of interest */ public boolean includesUpperBound( final int dim, final long binPos ) { return mappers.get( dim ).includesUpperBound( binPos ); } /** * Returns true if the given bin interval is closed on the left for the * given dimension. * * @param dim * The dimension of interest * @param binPos * The bin number of the interval of interest */ public boolean includesLowerBound( final int dim, final long binPos ) { return mappers.get( dim ).includesLowerBound( binPos ); } /** * Returns true if a given set of values are mapped to the lower tail of the * distribution. * * @param values * The set of values to determine the location of */ public boolean isInLowerTail( final List< T > values ) { for ( int i = 0; i < mappers.size(); i++ ) { if ( isInLowerTail( i, values.get( i ) ) ) { return true; } } return false; } /** * Returns true if a given value for a given dimension is mapped to the * lower tail of the distribution. * * @param dim * The dimension number of the axis of interest * @param value * The value to determine the location of */ public boolean isInLowerTail( final int dim, final T value ) { if ( hasTails( dim ) ) { final long binPos = mappers.get( dim ).map( value ); if ( binPos == 0 ) { return true; } } return false; } /** * Returns true if a given set of values are mapped to the upper tail of the * distribution. * * @param values * The set of values to determine the location of */ public boolean isInUpperTail( final List< T > values ) { for ( int i = 0; i < mappers.size(); i++ ) { if ( isInUpperTail( i, values.get( i ) ) ) { return true; } } return false; } /** * Returns true if a given value for a given dimension is mapped to the * upper tail of the distribution. * * @param dim * The dimension number of the axis of interest * @param value * The value to determine the location of */ public boolean isInUpperTail( final int dim, final T value ) { if ( hasTails( dim ) ) { final long binPos = mappers.get( dim ).map( value ); if ( binPos == mappers.get( dim ).getBinCount() - 1 ) { return true; } } return false; } /** * Returns true if a given set of values are mapped to the middle of the * distribution. * * @param values * The set of values to determine the location of */ public boolean isInMiddle( final List< T > values ) { for ( int i = 0; i < mappers.size(); i++ ) { if ( !isInMiddle( i, values.get( i ) ) ) { return false; } } return true; } /** * Returns true if a given value for a given dimension is mapped to the * middle of the distribution. * * @param dim * The dimension number of the axis of interest * @param value * The value to determine the location of */ public boolean isInMiddle( final int dim, final T value ) { if ( hasTails( dim ) ) { final long binPos = mappers.get( dim ).map( value ); if ( ( binPos == 0 ) || ( binPos == mappers.get( dim ).getBinCount() - 1 ) ) { return false; } } return true; } /** * Returns true if a given set of values are outside the distribution. * * @param values * The set of values to determine the location of */ public boolean isOutside( final List< T > values ) { for ( int i = 0; i < mappers.size(); i++ ) { if ( isOutside( i, values.get( i ) ) ) { return true; } } return false; } /** * Returns true if a given value for a given dimension is outside the * distribution. * * @param value * The value to determine the location of */ public boolean isOutside( final int dim, final T value ) { final long binPos = mappers.get( dim ).map( value ); if ( ( binPos == Long.MIN_VALUE ) || ( binPos == Long.MAX_VALUE ) ) { return true; } return false; } /** * Get the discrete frequency distribution associated with this histogram. */ public DiscreteFrequencyDistribution dfd() { return distrib; } /** * Counts the data contained in the given data source using the underlying * bin distribution. * * @param data * The total data to count */ public void countData( final Iterable< List< T >> data ) { init( data ); } /** * Counts the data contained in the given data source using the underlying * bin distribution. * * @param data * The total data to count */ public void countData( final List< Iterable< T >> data ) { init( data ); } /** * Counts additional data contained in a given iterable collection. One can * use this to update an existing histogram with a subset of values. * * @param data * The new data to count */ public void addData( final Iterable< List< T >> data ) { add( data ); } /** * Counts additional data contained in a given iterable collection. One can * use this to update an existing histogram with a subset of values. * * @param data * The new data to count */ public void addData( final List< Iterable< T >> data ) { add( data ); } /** * Uncounts some original data contained in a given iterable collection. One * can use this to update an existing histogram with a subset of values. * * @param data * The old data to uncount */ public void subtractData( final Iterable< List< T >> data ) { subtract( data ); } /** * Uncounts some original data contained in a given iterable collection. One * can use this to update an existing histogram with a subset of values. * * @param data * The old data to uncount */ public void subtractData( final List< Iterable< T >> data ) { subtract( data ); } /** * Directly increment a bin by position. * * @param binPos * The 1-d index of the bin */ public void increment( final long[] binPos ) { distrib.increment( binPos ); } /** * Directly decrement a bin by position. * * @param binPos * The 1-d index of the bin */ public void decrement( final long[] binPos ) { distrib.decrement( binPos ); } /** * Directly increment a bin by value. * * @param values * The values to map to a bin position */ public void increment( final List< T > values ) { count( values, incrementer ); } /** * Directly decrement a bin by value, * * @param values * The values to map to a bin position */ public void decrement( final List< T > values ) { count( values, decrementer ); } /** * Resets all data counts to 0. */ public void resetCounters() { reset(); } // -- delegated Img methods -- /** * Return the number of dimensions of the frequency distribution of this * histogram. */ @Override public int numDimensions() { return distrib.numDimensions(); } /** * Return the size of the given dimension of the frequency distribution of * this histogram. */ @Override public long dimension( final int d ) { return distrib.dimension( d ); } /** * Fill the provided long[] with the sizes of all dimensions of the * frequency distribution of this histogram. */ @Override public void dimensions( final long[] dims ) { distrib.dimensions( dims ); } @Override public RandomAccess< LongType > randomAccess() { return distrib.randomAccess(); } @Override public RandomAccess< LongType > randomAccess( final Interval interval ) { return distrib.randomAccess( interval ); } @Override public long min( final int d ) { return distrib.min( d ); } @Override public void min( final long[] min ) { distrib.min( min ); } @Override public void min( final Positionable min ) { distrib.min( min ); } @Override public long max( final int d ) { return distrib.max( d ); } @Override public void max( final long[] max ) { distrib.max( max ); } @Override public void max( final Positionable max ) { distrib.max( max ); } @Override public double realMin( final int d ) { return distrib.realMin( d ); } @Override public void realMin( final double[] min ) { distrib.realMin( min ); } @Override public void realMin( final RealPositionable min ) { distrib.realMin( min ); } @Override public double realMax( final int d ) { return distrib.realMax( d ); } @Override public void realMax( final double[] max ) { distrib.realMax( max ); } @Override public void realMax( final RealPositionable max ) { distrib.realMax( max ); } @Override public Cursor< LongType > cursor() { return distrib.cursor(); } @Override public Cursor< LongType > localizingCursor() { return distrib.localizingCursor(); } @Override public long size() { return distrib.size(); } @Override public LongType firstElement() { return distrib.firstElement(); } @Override public Object iterationOrder() { return distrib.iterationOrder(); } @Override public Iterator< LongType > iterator() { return distrib.iterator(); } @Override public ImgFactory< LongType > factory() { return distrib.factory(); } @Override public HistogramNd< T > copy() { return new HistogramNd< T >( this ); } // -- helpers -- private void reset() { distrib.resetCounters(); ignoredCount = 0; } private void init( final Iterable< List< T >> data ) { reset(); add( data ); } private void init( final List< Iterable< T >> data ) { reset(); add( data ); } private void add( final Iterable< List< T >> data ) { modifyCounts( data, incrementer ); } private void add( final List< Iterable< T >> data ) { modifyCounts( data, incrementer ); } private void subtract( final Iterable< List< T >> data ) { modifyCounts( data, decrementer ); } private void subtract( final List< Iterable< T >> data ) { modifyCounts( data, decrementer ); } private void modifyCounts( final Iterable< List< T >> data, final Counter counter ) { final Iterator< List< T >> iter = data.iterator(); while ( iter.hasNext() ) { count( iter.next(), counter ); } } private void modifyCounts( final List< Iterable< T >> data, final Counter counter ) { final List< T > vals = new ArrayList< T >( mappers.size() ); final List< Iterator< T >> iters = new ArrayList< Iterator< T >>(); for ( int i = 0; i < data.size(); i++ ) { iters.add( data.get( i ).iterator() ); vals.add( null ); } boolean hasNext = true; do { for ( int i = 0; i < iters.size(); i++ ) { if ( !iters.get( i ).hasNext() ) hasNext = false; } if ( hasNext ) { for ( int i = 0; i < iters.size(); i++ ) { vals.set( i, iters.get( i ).next() ); } count( vals, counter ); } } while ( hasNext ); } private void count( final List< T > values, final Counter counter ) { map( values, pos ); boolean ignored = false; for ( int i = 0; i < pos.length; i++ ) { if ( pos[ i ] == Long.MIN_VALUE || pos[ i ] == Long.MAX_VALUE ) { ignored = true; break; } } counter.count( pos, ignored ); } private interface Counter { void count( long[] pos, boolean ignored ); } private class Decrementer implements Counter { @SuppressWarnings( "synthetic-access" ) @Override public void count( final long[] position, final boolean ignored ) { if ( ignored ) ignoredCount--; else distrib.decrement( position ); } } private class Incrementer implements Counter { @SuppressWarnings( "synthetic-access" ) @Override public void count( final long[] position, final boolean ignored ) { if ( ignored ) ignoredCount++; else distrib.increment( position ); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy