diff --git a/scijava-ops-image/src/main/java/org/scijava/ops/image/filter/addNoise/NoiseAdders.java b/scijava-ops-image/src/main/java/org/scijava/ops/image/filter/addNoise/NoiseAdders.java index ce9a69e37..0c6152100 100644 --- a/scijava-ops-image/src/main/java/org/scijava/ops/image/filter/addNoise/NoiseAdders.java +++ b/scijava-ops-image/src/main/java/org/scijava/ops/image/filter/addNoise/NoiseAdders.java @@ -34,6 +34,7 @@ import net.imglib2.RandomAccessibleInterval; import net.imglib2.loops.LoopBuilder; import net.imglib2.type.numeric.RealType; +import org.scijava.common3.MersenneTwisterFast; import org.scijava.ops.spi.Nullable; /** @@ -164,4 +165,48 @@ public static , O extends RealType> void addPoissonNois output.setReal(k - 1); } + // -- UNIFORM NOISE -- // + + /** + * Sets the real component of an output real number to the addition of the real + * component of an input real number with an amount of uniform noise. + * + * @param input the input {@link RandomAccessibleInterval} + * @param rangeMin the "most negative" value that can be added to each element + * @param rangeMax the "most positive" value that can be added to each element + * @param seed the seed to the random number generator + * @param output the output {@link RandomAccessibleInterval} + * @implNote op names='filter.addUniformNoise', type=Computer + */ + public static > void addUniformNoise( // + RandomAccessibleInterval input, // + I rangeMin, // + I rangeMax, // + @Nullable Long seed, // + RandomAccessibleInterval output // + ) { + // Set seed to default if necessary + if (seed == null) { + seed = 0xabcdef1234567890L; + } + // Construct the Random Number Generator + MersenneTwisterFast rng = new MersenneTwisterFast(seed); + // Find the range + I range = rangeMax.createVariable(); + range.set(rangeMax); + range.sub(rangeMin); + + // Loop over the images + LoopBuilder.setImages(input, output).forEachPixel( (i, o) -> { + // Random value = next double * range + o.set(range); + o.mul(rng.nextDouble(true, true)); + // Add the range minimum + o.add(rangeMin); + // Add the original value + o.add(i); + }); + + } + } diff --git a/scijava-ops-image/src/test/java/org/scijava/ops/image/OpRegressionTest.java b/scijava-ops-image/src/test/java/org/scijava/ops/image/OpRegressionTest.java index 7639126b2..6d3db7acd 100644 --- a/scijava-ops-image/src/test/java/org/scijava/ops/image/OpRegressionTest.java +++ b/scijava-ops-image/src/test/java/org/scijava/ops/image/OpRegressionTest.java @@ -42,7 +42,7 @@ public class OpRegressionTest { @Test public void opDiscoveryRegressionIT() { - long expected = 1882; + long expected = 1884; long actual = ops.infos().size(); assertEquals(expected, actual); } diff --git a/scijava-ops-image/src/test/java/org/scijava/ops/image/filter/addNoise/NoiseAddersTest.java b/scijava-ops-image/src/test/java/org/scijava/ops/image/filter/addNoise/NoiseAddersTest.java new file mode 100644 index 000000000..29dcc6bb6 --- /dev/null +++ b/scijava-ops-image/src/test/java/org/scijava/ops/image/filter/addNoise/NoiseAddersTest.java @@ -0,0 +1,29 @@ +package org.scijava.ops.image.filter.addNoise; + +import net.imglib2.img.array.ArrayImgs; +import net.imglib2.type.numeric.integer.ByteType; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.scijava.ops.image.AbstractOpTest; + +import java.util.Arrays; +import java.util.List; + +public class NoiseAddersTest extends AbstractOpTest { + + @Test + public void testAddUniformNoiseRegression() { + var input = ArrayImgs.bytes(2, 2); + ops.unary("image.fill").input(new ByteType((byte) 1)).output(input).compute(); + var output = ArrayImgs.bytes(2, 2); + var rangeMin = new ByteType((byte) -1); + var rangeMax = new ByteType((byte) 1); + ops.ternary("filter.addUniformNoise").input(input, rangeMin, rangeMax).output(output).compute(); + var cursor = output.cursor(); + List expected = Arrays.asList((byte) 0, (byte) 2, (byte) 1, (byte) 1); + for(var e : expected) { + Assertions.assertEquals(e, cursor.next().get()); + } + Assertions.assertFalse(cursor.hasNext()); + } +}