Skip to content

Commit 2e12f57

Browse files
wiedenmctrueden
authored andcommitted
WIP: Port threshold (3)
TODO: Test for LocalMinErrorThreshold does not pass at the moment because we now throw exceptions instead of providing error messages via secondary outputs. This might be okay, but could also be considered a regression.
1 parent 39b0d21 commit 2e12f57

18 files changed

Lines changed: 979 additions & 458 deletions

src/main/java/net/imagej/ops/filter/AbstractCenterAwareNeighborhoodBasedFilter.java renamed to src/main/java/net/imagej/ops/filter/ApplyCenterAwareNeighborhoodBasedFilter.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
import org.scijava.ops.core.computer.BiComputer;
4242
import org.scijava.param.Mutable;
4343

44-
public abstract class AbstractCenterAwareNeighborhoodBasedFilter<I, O> {
44+
public final class ApplyCenterAwareNeighborhoodBasedFilter<I, O> {
4545

4646
private static final OutOfBoundsFactory<?, ?> DEFAULT_OUT_OF_BOUNDS_FACTORY =
4747
new OutOfBoundsBorderFactory<>();
@@ -53,7 +53,11 @@ public abstract class AbstractCenterAwareNeighborhoodBasedFilter<I, O> {
5353
return (OutOfBoundsFactory<I, RandomAccessibleInterval<I>>) DEFAULT_OUT_OF_BOUNDS_FACTORY;
5454
}
5555

56-
protected void computeInternal(final RandomAccessibleInterval<I> input,
56+
private ApplyCenterAwareNeighborhoodBasedFilter() {
57+
// Utility class
58+
}
59+
60+
public static <I, O> void compute(final RandomAccessibleInterval<I> input,
5761
final Shape inputNeighborhoodShape,
5862
OutOfBoundsFactory<I, RandomAccessibleInterval<I>> outOfBoundsFactory,
5963
final BiComputer<Iterable<I>, I, O> filterOp,
@@ -75,7 +79,9 @@ private static <I1, I2, O> void map(
7579
{
7680
// TODO: This used to be done via a net.imagej.ops.Ops.Map meta op. We may
7781
// want to revert to that approach if this proves to be too inflexible.
78-
// (Parallelization would be useful, for instance.)
82+
// (Parallelization would be useful, for instance.) In this case, we would
83+
// need to make this static class a proper op, again (or let clients pass a
84+
// mapper op).
7985
final Cursor<? extends I1> neighborhoodCursor = inputNeighborhoods
8086
.localizingCursor();
8187
final RandomAccess<I2> centerPixelsAccess = inputCenterPixels

src/main/java/net/imagej/ops/threshold/LocalThresholdIntegral.java renamed to src/main/java/net/imagej/ops/threshold/ApplyLocalThresholdIntegral.java

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,11 @@
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

Comments
 (0)