001/**
002 * 
003 */
004package plugins.kernel.roi.descriptor.intensity;
005
006import icy.plugin.abstract_.Plugin;
007import icy.plugin.interface_.PluginROIDescriptor;
008import icy.roi.ROI;
009import icy.roi.ROIDescriptor;
010import icy.sequence.Sequence;
011import icy.sequence.SequenceDataIterator;
012
013import java.util.ArrayList;
014import java.util.HashMap;
015import java.util.List;
016import java.util.Map;
017
018/**
019 * This {@link PluginROIDescriptor} implements the following "intensity" ROI descriptors:<br/>
020 * <li>Minimum intensity</li><br/>
021 * <li>Mean intensity</li><br/>
022 * <li>Maximum intensity</li><br/>
023 * <li>Sum intensity</li><br/>
024 * <li>Standard deviation</li><br/>
025 * 
026 * @author Stephane
027 */
028public class ROIIntensityDescriptorsPlugin extends Plugin implements PluginROIDescriptor
029{
030    public static final String ID_MIN_INTENSITY = ROIMinIntensityDescriptor.ID;
031    public static final String ID_MEAN_INTENSITY = ROIMeanIntensityDescriptor.ID;
032    public static final String ID_MAX_INTENSITY = ROIMaxIntensityDescriptor.ID;
033    public static final String ID_SUM_INTENSITY = ROISumIntensityDescriptor.ID;
034    public static final String ID_STANDARD_DEVIATION = ROIStandardDeviationDescriptor.ID;
035
036    public static final ROIMinIntensityDescriptor minIntensityDescriptor = new ROIMinIntensityDescriptor();
037    public static final ROIMeanIntensityDescriptor meanIntensityDescriptor = new ROIMeanIntensityDescriptor();
038    public static final ROIMaxIntensityDescriptor maxIntensityDescriptor = new ROIMaxIntensityDescriptor();
039    public static final ROISumIntensityDescriptor sumIntensityDescriptor = new ROISumIntensityDescriptor();
040    public static final ROIStandardDeviationDescriptor standardDeviationDescriptor = new ROIStandardDeviationDescriptor();
041
042    public static class IntensityDescriptorInfos
043    {
044        public double min;
045        public double mean;
046        public double max;
047        public double sum;
048        public double deviation;
049    };
050
051    /**
052     * Returns the pixel intensity information for the specified ROI and Sequence.<br>
053     * Be careful: the returned result may be incorrect or exception may be thrown if the ROI change while the
054     * descriptor is being computed.
055     * 
056     * @param roi
057     *        the ROI on which we want to compute the intensity descriptors
058     * @param sequence
059     *        the Sequence used to compute the intensity descriptors
060     * @param allowMultiChannel
061     *        Allow multi channel intensity computation. If this parameter is set to <code>false</code> and the ROI
062     *        number of channel is > 1 then a {@link UnsupportedOperationException} is launch.
063     * @throws Exception
064     *         If the ROI dimension changed during the descriptor computation.
065     * @throws UnsupportedOperationException
066     *         If the C dimension of the ROI is > 1 while allowMultiChannel parameter is set to <code>false</code>
067     */
068    public static IntensityDescriptorInfos computeIntensityDescriptors(ROI roi, Sequence sequence,
069            boolean allowMultiChannel) throws Exception, UnsupportedOperationException
070    {
071        if (!allowMultiChannel && (roi.getBounds5D().getSizeC() > 1d))
072            throw new UnsupportedOperationException(
073                    "Not allowed to cannot compute intensity descriptor on a multi channel ROI (sizeC > 1).");
074
075        final IntensityDescriptorInfos result = new IntensityDescriptorInfos();
076
077        long numPixels = 0;
078        double min = Double.MAX_VALUE;
079        double max = -Double.MAX_VALUE;
080        double sum = 0;
081        double sum2 = 0;
082
083        // FIXME: we were using interior pixels only, now we also use edge pixels so we can have intensities info
084        // for intersection only ROI --> see if that is a good idea...
085        final SequenceDataIterator it = new SequenceDataIterator(sequence, roi, true);
086
087        while (!it.done())
088        {
089            final double value = it.get();
090
091            if (min > value)
092                min = value;
093            if (max < value)
094                max = value;
095            sum += value;
096            sum2 += value * value;
097            numPixels++;
098
099            it.next();
100        }
101
102        if (numPixels > 0)
103        {
104            result.min = min;
105            result.max = max;
106            result.sum = sum;
107
108            final double mean = sum / numPixels;
109            final double x1 = (sum2 / numPixels);
110            final double x2 = mean * mean;
111
112            result.mean = mean;
113            result.deviation = Math.sqrt(x1 - x2);
114        }
115        else
116        {
117            result.min = 0d;
118            result.mean = 0d;
119            result.max = 0d;
120            result.sum = 0d;
121            result.deviation = 0d;
122        }
123
124        return result;
125    }
126
127    @Override
128    public List<ROIDescriptor> getDescriptors()
129    {
130        final List<ROIDescriptor> result = new ArrayList<ROIDescriptor>();
131
132        result.add(minIntensityDescriptor);
133        result.add(meanIntensityDescriptor);
134        result.add(maxIntensityDescriptor);
135        result.add(sumIntensityDescriptor);
136        result.add(standardDeviationDescriptor);
137
138        return result;
139    }
140
141    @Override
142    public Map<ROIDescriptor, Object> compute(ROI roi, Sequence sequence) throws UnsupportedOperationException
143    {
144        final Map<ROIDescriptor, Object> result = new HashMap<ROIDescriptor, Object>();
145        try
146        {
147            // compute intensity descriptors
148            final IntensityDescriptorInfos intensityInfos = computeIntensityDescriptors(roi, sequence, false);
149
150            result.put(minIntensityDescriptor, Double.valueOf(intensityInfos.min));
151            result.put(meanIntensityDescriptor, Double.valueOf(intensityInfos.mean));
152            result.put(maxIntensityDescriptor, Double.valueOf(intensityInfos.max));
153            result.put(sumIntensityDescriptor, Double.valueOf(intensityInfos.sum));
154            result.put(standardDeviationDescriptor, Double.valueOf(intensityInfos.deviation));
155        }
156        catch (Exception e)
157        {
158            throw new UnsupportedOperationException(getClass().getSimpleName() + ": cannot compute descriptors for '"
159                    + roi.getName() + "'", e);
160        }
161
162        return result;
163    }
164}