/**
*
*/
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;
}
}