001/*-
002 *******************************************************************************
003 * Copyright (c) 2011, 2016 Diamond Light Source Ltd.
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *    Peter Chang - initial API and implementation and/or initial documentation
011 *******************************************************************************/
012
013package org.eclipse.january.dataset;
014
015import java.util.Arrays;
016import java.util.Collection;
017import java.util.Iterator;
018import java.util.List;
019
020/**
021 * Statistics of data set lists. Used for image processing.
022 */
023public class CollectionStats {
024
025        private static interface StatFunction {
026                double evaluate(Dataset set);
027        }
028
029        /**
030         * Used to get a mean image from a set of images for instance.
031         * 
032         * @param sets datasets
033         * @return mean dataset of the same shape as those passed in
034         * @throws Exception when datasets have different shapes
035         */
036        public static Dataset mean(final List<IDataset> sets) throws Exception {
037                
038                return process(sets, new StatFunction() {
039                        @Override
040                        public double evaluate(Dataset set) {
041                                return (Double)set.mean();
042                        }
043                });
044        }
045        
046        /**
047         * Used to get a median image from a set of images for instance.
048         * 
049         * @param sets datasets
050         * @return median dataset of the same shape as those passed in
051         * @throws Exception when datasets have different shapes
052         */
053        public static Dataset median(final List<IDataset> sets) throws Exception {
054                
055                return process(sets, new StatFunction() {
056                        @Override
057                        public double evaluate(Dataset set) {
058                                return (Double)Stats.median(set);
059                        }
060                });
061        }
062
063        /**
064         * Used to get a median image from a set of images for instance.
065         * 
066         * @param sets datasets
067         * @return median data set of the same shape as those passed in.
068         * @throws Exception when datasets have different shapes
069         */
070        private static Dataset process(final List<IDataset> sets, final StatFunction function) throws Exception {
071                
072                int[] shape = assertShapes(sets);
073                final DoubleDataset result = DatasetFactory.zeros(DoubleDataset.class, shape);
074                final double[] rData = result.getData();
075                final IndexIterator iter = new PositionIterator(shape);
076                final int[] pos = iter.getPos();
077
078                final int len = sets.size();
079                final DoubleDataset pixel = DatasetFactory.zeros(DoubleDataset.class, len);
080                final double[] pData = pixel.getData();
081                for (int i = 0; iter.hasNext(); i++) {
082                        for (int ipix = 0; ipix < len; ipix++) {
083                                pData[ipix] = sets.get(ipix).getDouble(pos);
084                        }
085                        pixel.setDirty();
086                        rData[i] = function.evaluate(pixel);
087                }
088
089                return result;
090        }
091
092        private static int[] assertShapes(final Collection<IDataset> sets) throws Exception {
093                
094                if (sets.size()<2) throw new IllegalArgumentException("You must take the median of at least two sets!");
095                
096                final Iterator<IDataset> it = sets.iterator();
097                final int[] shape = it.next().getShape();
098                while (it.hasNext()) {
099                        IDataset d = it.next();
100                        final int[] nextShape = d.getShape();
101                        if (!Arrays.equals(shape, nextShape)) throw new IllegalArgumentException("All data sets should be the same shape!");
102                }
103                return shape;
104        }
105}