/** * */ package plugins.kernel.roi.descriptor.intensity; import icy.plugin.abstract_.Plugin; import icy.plugin.interface_.PluginROIDescriptor; import icy.roi.ROI; import icy.roi.ROIDescriptor; import icy.sequence.Sequence; import icy.sequence.SequenceDataIterator; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * This {@link PluginROIDescriptor} implements the following "intensity" ROI descriptors:<br/> * <li>Minimum intensity</li><br/> * <li>Mean intensity</li><br/> * <li>Maximum intensity</li><br/> * <li>Sum intensity</li><br/> * <li>Standard deviation</li><br/> * * @author Stephane */ public class ROIIntensityDescriptorsPlugin extends Plugin implements PluginROIDescriptor { public static final String ID_MIN_INTENSITY = ROIMinIntensityDescriptor.ID; public static final String ID_MEAN_INTENSITY = ROIMeanIntensityDescriptor.ID; public static final String ID_MAX_INTENSITY = ROIMaxIntensityDescriptor.ID; public static final String ID_SUM_INTENSITY = ROISumIntensityDescriptor.ID; public static final String ID_STANDARD_DEVIATION = ROIStandardDeviationDescriptor.ID; public static final ROIMinIntensityDescriptor minIntensityDescriptor = new ROIMinIntensityDescriptor(); public static final ROIMeanIntensityDescriptor meanIntensityDescriptor = new ROIMeanIntensityDescriptor(); public static final ROIMaxIntensityDescriptor maxIntensityDescriptor = new ROIMaxIntensityDescriptor(); public static final ROISumIntensityDescriptor sumIntensityDescriptor = new ROISumIntensityDescriptor(); public static final ROIStandardDeviationDescriptor standardDeviationDescriptor = new ROIStandardDeviationDescriptor(); public static class IntensityDescriptorInfos { public double min; public double mean; public double max; public double sum; public double deviation; }; /** * Returns the pixel intensity information for the specified ROI and Sequence.<br> * Be careful: the returned result may be incorrect or exception may be thrown if the ROI change while the * descriptor is being computed. * * @param roi * the ROI on which we want to compute the intensity descriptors * @param sequence * the Sequence used to compute the intensity descriptors * @param allowMultiChannel * Allow multi channel intensity computation. If this parameter is set to <code>false</code> and the ROI * number of channel is > 1 then a {@link UnsupportedOperationException} is launch. * @throws Exception * If the ROI dimension changed during the descriptor computation. * @throws UnsupportedOperationException * If the C dimension of the ROI is > 1 while allowMultiChannel parameter is set to <code>false</code> */ public static IntensityDescriptorInfos computeIntensityDescriptors(ROI roi, Sequence sequence, boolean allowMultiChannel) throws Exception, UnsupportedOperationException { if (!allowMultiChannel && (roi.getBounds5D().getSizeC() > 1d)) throw new UnsupportedOperationException( "Not allowed to cannot compute intensity descriptor on a multi channel ROI (sizeC > 1)."); final IntensityDescriptorInfos result = new IntensityDescriptorInfos(); long numPixels = 0; double min = Double.MAX_VALUE; double max = -Double.MAX_VALUE; double sum = 0; double sum2 = 0; // FIXME: we were using interior pixels only, now we also use edge pixels so we can have intensities info // for intersection only ROI --> see if that is a good idea... final SequenceDataIterator it = new SequenceDataIterator(sequence, roi, true); while (!it.done()) { final double value = it.get(); if (min > value) min = value; if (max < value) max = value; sum += value; sum2 += value * value; numPixels++; it.next(); } if (numPixels > 0) { result.min = min; result.max = max; result.sum = sum; final double mean = sum / numPixels; final double x1 = (sum2 / numPixels); final double x2 = mean * mean; result.mean = mean; result.deviation = Math.sqrt(x1 - x2); } else { result.min = 0d; result.mean = 0d; result.max = 0d; result.sum = 0d; result.deviation = 0d; } return result; } @Override public List<ROIDescriptor> getDescriptors() { final List<ROIDescriptor> result = new ArrayList<ROIDescriptor>(); result.add(minIntensityDescriptor); result.add(meanIntensityDescriptor); result.add(maxIntensityDescriptor); result.add(sumIntensityDescriptor); result.add(standardDeviationDescriptor); return result; } @Override public Map<ROIDescriptor, Object> compute(ROI roi, Sequence sequence) throws UnsupportedOperationException { final Map<ROIDescriptor, Object> result = new HashMap<ROIDescriptor, Object>(); try { // compute intensity descriptors final IntensityDescriptorInfos intensityInfos = computeIntensityDescriptors(roi, sequence, false); result.put(minIntensityDescriptor, Double.valueOf(intensityInfos.min)); result.put(meanIntensityDescriptor, Double.valueOf(intensityInfos.mean)); result.put(maxIntensityDescriptor, Double.valueOf(intensityInfos.max)); result.put(sumIntensityDescriptor, Double.valueOf(intensityInfos.sum)); result.put(standardDeviationDescriptor, Double.valueOf(intensityInfos.deviation)); } catch (Exception e) { throw new UnsupportedOperationException(getClass().getSimpleName() + ": cannot compute descriptors for '" + roi.getName() + "'", e); } return result; } }