|
| 1 | + |
| 2 | +package net.imagej.ops2.tutorial; |
| 3 | + |
| 4 | +import java.util.stream.Collectors; |
| 5 | +import java.util.stream.IntStream; |
| 6 | + |
| 7 | +import org.scijava.function.Computers; |
| 8 | +import org.scijava.ops.api.OpEnvironment; |
| 9 | +import org.scijava.ops.engine.DefaultOpEnvironment; |
| 10 | +import org.scijava.ops.spi.OpCollection; |
| 11 | +import org.scijava.ops.spi.OpMethod; |
| 12 | + |
| 13 | +import net.imglib2.Interval; |
| 14 | +import net.imglib2.algorithm.neighborhood.Neighborhood; |
| 15 | +import net.imglib2.algorithm.neighborhood.RectangleShape; |
| 16 | +import net.imglib2.algorithm.neighborhood.Shape; |
| 17 | +import net.imglib2.img.Img; |
| 18 | +import net.imglib2.img.array.ArrayImgs; |
| 19 | +import net.imglib2.loops.IntervalChunks; |
| 20 | +import net.imglib2.outofbounds.OutOfBoundsConstantValueFactory; |
| 21 | +import net.imglib2.parallel.Parallelization; |
| 22 | +import net.imglib2.type.numeric.integer.UnsignedByteType; |
| 23 | +import net.imglib2.view.Views; |
| 24 | + |
| 25 | +/** |
| 26 | + * SciJava Ops includes a mechanism for automatically introducing concurrency to |
| 27 | + * Ops. |
| 28 | + * |
| 29 | + * Developers can utilize this mechanism by writing their Ops on the smallest |
| 30 | + * element of the computation, be that a single pixel, or a {@link Neighborhood}. |
| 31 | + * SciJava Ops will then "lift" these Ops, creating parallelized Ops that run |
| 32 | + * on an entire {@link net.imglib2.RandomAccessibleInterval} |
| 33 | + * |
| 34 | + * This tutorial showcases these lifting mechanisms. |
| 35 | + * |
| 36 | + * @author Gabriel Selzer |
| 37 | + */ |
| 38 | +public class OpParallelization implements OpCollection { |
| 39 | + |
| 40 | + /** |
| 41 | + * This Op, which is really just a computation on a single pixel, lets the |
| 42 | + * framework assume the burden of parallelization |
| 43 | + * |
| 44 | + * @param input the input pixel |
| 45 | + * @param output the preallocated output pixel |
| 46 | + */ |
| 47 | + @OpMethod(names = "tutorial.invertPerPixel", type = Computers.Arity1.class) |
| 48 | + public static void invertOp(UnsignedByteType input, UnsignedByteType output) { |
| 49 | + output.set(255 - input.get()); |
| 50 | + } |
| 51 | + |
| 52 | + /** |
| 53 | + * This Op, which computes some algorithm over a neighborhood, also lets the |
| 54 | + * framework assume the burden of parallelization |
| 55 | + * |
| 56 | + * @param input the input pixel |
| 57 | + * @param output the preallocated output pixel |
| 58 | + */ |
| 59 | + @OpMethod(names = "tutorial.neighborhoodAverage", |
| 60 | + type = Computers.Arity1.class) |
| 61 | + public static void averageNeighborhood(Neighborhood<UnsignedByteType> input, |
| 62 | + UnsignedByteType output) |
| 63 | + { |
| 64 | + long tmp = 0; |
| 65 | + var cursor = input.cursor(); |
| 66 | + while (cursor.hasNext()) { |
| 67 | + tmp += cursor.next().getIntegerLong(); |
| 68 | + } |
| 69 | + output.setInteger(tmp / input.size()); |
| 70 | + } |
| 71 | + |
| 72 | + public static void main(String... args) { |
| 73 | + OpEnvironment ops = new DefaultOpEnvironment(); |
| 74 | + |
| 75 | + // First, we show parallelization at work for our per-pixel Op. |
| 76 | + // SciJava Ops understands how to apply that Op to each pixel of the input |
| 77 | + // image |
| 78 | + |
| 79 | + // Fill an input image with a value |
| 80 | + var fillValue = new UnsignedByteType(5); |
| 81 | + var inImg = ArrayImgs.unsignedBytes(10, 10); |
| 82 | + ops.op("image.fill").arity1().input(fillValue).output(inImg).compute(); |
| 83 | + // Run the Op |
| 84 | + var outImg = ArrayImgs.unsignedBytes(10, 10); |
| 85 | + ops.op("tutorial.invertPerPixel").arity1().input(inImg).output(outImg) |
| 86 | + .compute(); |
| 87 | + // Get the original value, and the inverted value |
| 88 | + var original = inImg.firstElement().get(); |
| 89 | + var inverted = outImg.firstElement().get(); |
| 90 | + System.out.println("Original image was filled with value " + original + |
| 91 | + ", and the inverted image is filled with value (255 - " + original + |
| 92 | + ") = " + inverted); |
| 93 | + |
| 94 | + // Now, we show parallelization at work for our Parallelization Op. |
| 95 | + // For this example, we use a radius-1 rectangle; in other words, the |
| 96 | + // neighborhood |
| 97 | + // for a given pixel includes all of its immediate neighbors (including |
| 98 | + // diagonal) |
| 99 | + var shape = new RectangleShape(1, false); |
| 100 | + ops.op("tutorial.neighborhoodAverage").arity2().input(inImg, shape) |
| 101 | + .output(outImg).compute(); |
| 102 | + // Get the original value, and the radius-1 neighborhood value |
| 103 | + original = inImg.firstElement().get(); |
| 104 | + var mean = outImg.firstElement().get(); |
| 105 | + System.out.println("Original image was filled with value " + original + |
| 106 | + ", and the radius-1 mean at the corner is (4 * " + original + " / 9) = " + |
| 107 | + mean); |
| 108 | + } |
| 109 | + |
| 110 | +} |
0 commit comments