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}