6060
6161/**
6262 * Apply a local thresholding method to an image using integral images for speed
63- * up, optionally using a out of bounds strategy.
63+ * up, optionally using an out of bounds strategy.
6464 *
6565 * @author Stefan Helfrich (University of Konstanz)
6666 */
67- public abstract class LocalThresholdIntegral <T extends RealType <T >> {
67+ public abstract class ApplyLocalThresholdIntegral <T extends RealType <T >> {
6868
6969 private static final OutOfBoundsFactory <?, ?> DEFAULT_OUT_OF_BOUNDS_FACTORY =
7070 new OutOfBoundsBorderFactory <>();
@@ -76,22 +76,34 @@ public abstract class LocalThresholdIntegral<T extends RealType<T>> {
7676 return (OutOfBoundsFactory <I , RandomAccessibleInterval <I >>) DEFAULT_OUT_OF_BOUNDS_FACTORY ;
7777 }
7878
79- private final int [] requiredIntegralImageOrders ;
79+ // TODO: The only reason this class is not fully static (but also serves as
80+ // abstract base class), is to be able to use op dependencies here to avoid
81+ // boilerplate code in extending classes. Is there some better way to do this?
8082
8183 @ OpDependency (name = "image.integral" )
8284 private Function <RandomAccessibleInterval <T >, RandomAccessibleInterval <RealType <?>>> integralImgOp ;
8385
8486 @ OpDependency (name = "image.squareIntegral" )
8587 private Function <RandomAccessibleInterval <T >, RandomAccessibleInterval <RealType <?>>> squareIntegralImgOp ;
8688
87- public LocalThresholdIntegral (final int [] requiredIntegralImageOrders ) {
88- this .requiredIntegralImageOrders = requiredIntegralImageOrders ;
89+ protected
90+ Function <RandomAccessibleInterval <T >, RandomAccessibleInterval <RealType <?>>>
91+ getIntegralImageOp (final int integralImageOrder )
92+ {
93+ if (integralImageOrder == 1 ) return integralImgOp ;
94+ else if (integralImageOrder == 2 ) return squareIntegralImgOp ;
95+ else throw new OpExecutionException (
96+ "Threshold op requires to compute an integral image of order " +
97+ integralImageOrder +
98+ ". There is no op available to do that (available orders are: 1, 2)." );
8999 }
90100
91101 @ SuppressWarnings ("rawtypes" )
92- protected void computeInternal (final RandomAccessibleInterval <T > input ,
102+ public static <T extends RealType <T >> void compute (
103+ final RandomAccessibleInterval <T > input ,
93104 RectangleShape inputNeighborhoodShape ,
94105 OutOfBoundsFactory <T , RandomAccessibleInterval <T >> outOfBoundsFactory ,
106+ final List <Function <RandomAccessibleInterval <T >, RandomAccessibleInterval <RealType <?>>>> integralImageOps ,
95107 final BiComputer <RectangleNeighborhood <Composite <DoubleType >>, T , BitType > thresholdOp ,
96108 final IterableInterval <BitType > output )
97109 {
@@ -103,15 +115,16 @@ protected void computeInternal(final RandomAccessibleInterval<T> input,
103115 .getSpan () + 1 , false );
104116
105117 final List <RandomAccessibleInterval <RealType >> listOfIntegralImages =
106- new ArrayList <>();
107- for (final int order : requiredIntegralImageOrders ) {
118+ new ArrayList <>(integralImageOps .size ());
119+ for (final Function <RandomAccessibleInterval <T >, RandomAccessibleInterval <RealType <?>>> //
120+ integralImageOp : integralImageOps ) {
108121 final RandomAccessibleInterval <RealType > requiredIntegralImg =
109122 getIntegralImage (input , inputNeighborhoodShape , outOfBoundsFactory ,
110- order );
123+ integralImageOp );
111124 listOfIntegralImages .add (requiredIntegralImg );
112125 }
113126
114- // Composite image of integral images of order 1 and 2
127+ // Composite image of integral images of all orders
115128 final RandomAccessibleInterval <RealType > stacked = Views .stack (
116129 listOfIntegralImages );
117130 final RandomAccessibleInterval <? extends Composite <RealType >> compositeRAI =
@@ -131,36 +144,23 @@ protected void computeInternal(final RandomAccessibleInterval<T> input,
131144 * {@link IntegralMean} et al work with them.
132145 *
133146 * @param input The RAI for which an integral image is computed
134- * @param order
135147 * @return An extended integral image for the input RAI
136148 */
137149 @ SuppressWarnings ({ "unchecked" , "rawtypes" })
138- private RandomAccessibleInterval <RealType > getIntegralImage (
139- final RandomAccessibleInterval <T > input , final RectangleShape shape ,
140- final OutOfBoundsFactory <T , RandomAccessibleInterval <T >> outOfBoundsFactory ,
141- final int order )
150+ private static <T extends RealType <T >> RandomAccessibleInterval <RealType >
151+ getIntegralImage (final RandomAccessibleInterval <T > input ,
152+ final RectangleShape shape ,
153+ final OutOfBoundsFactory <T , RandomAccessibleInterval <T >> outOfBoundsFactory ,
154+ final Function <RandomAccessibleInterval <T >, RandomAccessibleInterval <RealType <?>>> integralOp )
142155 {
143156 final ExtendedRandomAccessibleInterval <T , RandomAccessibleInterval <T >> extendedInput =
144157 Views .extend (input , outOfBoundsFactory );
145158 final FinalInterval expandedInterval = Intervals .expand (input , shape
146159 .getSpan () - 1l );
147160 final IntervalView <T > offsetInterval2 = Views .offsetInterval (extendedInput ,
148161 expandedInterval );
149-
150- RandomAccessibleInterval <RealType > img = null ;
151- switch (order ) {
152- case 1 :
153- img = (RandomAccessibleInterval ) integralImgOp .apply (offsetInterval2 );
154- break ;
155- case 2 :
156- img = (RandomAccessibleInterval ) squareIntegralImgOp .apply (
157- offsetInterval2 );
158- break ;
159- default :
160- throw new OpExecutionException (
161- "Threshold op requires an integral image of order " + order +
162- ". This is not available (available orders: 1, 2)." );
163- }
162+ final RandomAccessibleInterval <RealType > img =
163+ (RandomAccessibleInterval ) integralOp .apply (offsetInterval2 );
164164 return addLeadingZeros (img );
165165 }
166166
@@ -223,6 +223,9 @@ private static <I1, I2, O> void map(
223223 {
224224 // TODO: This used to be done via a net.imagej.ops.Ops.Map meta op. We may
225225 // want to revert to that approach if this proves to be too inflexible.
226+ // (Parallelization would be useful, for instance.) In this case, we would
227+ // need to make this static method part of a proper op, again (or let
228+ // clients pass a mapper op).
226229 final Cursor <? extends I1 > neighborhoodCursor = inputNeighborhoods
227230 .localizingCursor ();
228231 final RandomAccess <I2 > centerPixelsAccess = inputCenterPixels
0 commit comments