Skip to content

Commit 5d06e5d

Browse files
committed
Convert SACA PNorm helper class into an Op
This commit converts the PNorm.java helper class into an Op in the `stats` namespace. This commit also adds a test for this new Op.
1 parent 6c8e275 commit 5d06e5d

File tree

4 files changed

+111
-90
lines changed

4 files changed

+111
-90
lines changed

scijava-ops-image/src/main/java/org/scijava/ops/image/coloc/saca/SACAHeatmapPValue.java

Lines changed: 0 additions & 73 deletions
This file was deleted.

scijava-ops-image/src/main/java/org/scijava/ops/image/coloc/saca/PNorm.java renamed to scijava-ops-image/src/main/java/org/scijava/ops/image/stats/DefaultPNorm.java

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,39 +27,53 @@
2727
* #L%
2828
*/
2929

30-
package org.scijava.ops.image.coloc.saca;
30+
package org.scijava.ops.image.stats;
31+
32+
import org.apache.commons.math3.distribution.NormalDistribution;
3133

3234
import net.imglib2.loops.LoopBuilder;
3335
import net.imglib2.RandomAccessibleInterval;
3436
import net.imglib2.type.numeric.real.DoubleType;
3537

36-
import org.apache.commons.math3.distribution.NormalDistribution;
38+
import org.scijava.function.Computers;
39+
import org.scijava.ops.spi.Nullable;
3740

3841
/**
39-
* Helper class for Spatially Adaptive Colocalization Analysis (SACA) framework.
40-
* This class is used by the "coloc.saca.heatmapPValue" Op to produce a heatmap
41-
* of pixelwise p-values. This class replicates R's pnorm function.
42-
*
4342
* @author Edward Evans
43+
* @implNote op names='stats.pnorm', priority='100.'
4444
*/
4545

46-
public final class PNorm {
47-
48-
private PNorm() {}
46+
public final class DefaultPNorm implements
47+
Computers.Arity2<RandomAccessibleInterval<DoubleType>, Boolean, RandomAccessibleInterval<DoubleType>>
48+
{
4949

50-
public static void compute(RandomAccessibleInterval<DoubleType> input,
51-
boolean lowerTail, RandomAccessibleInterval<DoubleType> output)
50+
/**
51+
* Op to calculate the pixel-wise cumulative probability of a normal
52+
* distribution over an image.
53+
*
54+
* @param input input image
55+
* @param lowerTail indicates whether to compute the lower tail cumulative
56+
* probability
57+
* @param result the pixel-wise normal distribution over the image
58+
*/
59+
@Override
60+
public void compute(final RandomAccessibleInterval<DoubleType> input,
61+
@Nullable Boolean lowerTail, RandomAccessibleInterval<DoubleType> result)
5262
{
63+
// set lowerTail if necessary
64+
if (lowerTail == null) lowerTail = true;
65+
final boolean finalLowerTail = lowerTail;
66+
5367
// compute normal distribution over the image
5468
NormalDistribution normalDistribution = new NormalDistribution();
5569

56-
LoopBuilder.setImages(input, output).multiThreaded().forEachPixel((i,
57-
o) -> {
70+
LoopBuilder.setImages(input, result).multiThreaded().forEachPixel((i,
71+
r) -> {
5872
double normDistValue = normalDistribution.cumulativeProbability(i.get());
59-
if (!lowerTail) {
73+
if (!finalLowerTail) {
6074
normDistValue = 1 - normDistValue;
6175
}
62-
o.set(normDistValue);
76+
r.set(normDistValue);
6377
});
6478
}
6579
}

scijava-ops-image/src/test/java/org/scijava/ops/image/coloc/saca/SACATest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ public void testSACAFramework() {
8181
// run SACA heatmap Z score op
8282
ops.op("coloc.saca.heatmapZScore").input(gs, rs).output(zscore).compute();
8383

84-
// run SACA heatmap p value op
85-
ops.op("coloc.saca.heatmapPValue").input(zscore).output(pvalue).compute();
84+
// run stats.pnorm op
85+
ops.op("stats.pnorm").input(zscore).output(pvalue).compute();
8686

8787
// run SACA significant pixel mask op
8888
ops.op("coloc.saca.sigMask").input(zscore).output(sigMask).compute();
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*-
2+
* #%L
3+
* Image processing operations for SciJava Ops.
4+
* %%
5+
* Copyright (C) 2014 - 2024 SciJava developers.
6+
* %%
7+
* Redistribution and use in source and binary forms, with or without
8+
* modification, are permitted provided that the following conditions are met:
9+
*
10+
* 1. Redistributions of source code must retain the above copyright notice,
11+
* this list of conditions and the following disclaimer.
12+
* 2. Redistributions in binary form must reproduce the above copyright notice,
13+
* this list of conditions and the following disclaimer in the documentation
14+
* and/or other materials provided with the distribution.
15+
*
16+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
20+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26+
* POSSIBILITY OF SUCH DAMAGE.
27+
* #L%
28+
*/
29+
30+
package org.scijava.ops.image.stats;
31+
32+
import net.imglib2.RandomAccess;
33+
import net.imglib2.img.Img;
34+
import net.imglib2.type.numeric.real.DoubleType;
35+
import net.imglib2.type.numeric.real.FloatType;
36+
37+
import org.scijava.ops.image.AbstractOpTest;
38+
import org.scijava.types.Nil;
39+
40+
import org.junit.jupiter.api.Test;
41+
import static org.junit.jupiter.api.Assertions.assertEquals;
42+
43+
/**
44+
* Test {@code stats.pnorm} op.
45+
*
46+
* @author Edward Evans
47+
*/
48+
49+
public class DefaultPNormTest extends AbstractOpTest {
50+
51+
@Test
52+
public void testImg() {
53+
final int[] xPositions = { 30, 79, 77, 104, 7, 52, 164, 88, 119, 65 };
54+
final int[] yPositions = { 30, 36, 80, 79, 139, 102, 77, 41, 142, 118 };
55+
final double[] pvalueExpected = { 0.5, 0.9999998132675161, 0.5,
56+
0.8861427670894226, 0.5, 0.9999999999828452, 0.5, 0.9960363221624154, 0.5,
57+
0.999998128463855 };
58+
59+
// load Z-score heatmap slice
60+
Img<FloatType> zscore = openFloatImg("zscore_test_data.tif");
61+
62+
// create p-value image container
63+
Img<DoubleType> pvalue = ops.op("create.img").input(zscore,
64+
new DoubleType()).outType(new Nil<Img<DoubleType>>()
65+
{}).apply();
66+
67+
// run stats.pnorm op on Z-score data
68+
ops.op("stats.pnorm").input(zscore).output(pvalue).compute();
69+
70+
// get random access and compare pixels
71+
final RandomAccess<DoubleType> pRA = pvalue.randomAccess();
72+
73+
// assert results are equal
74+
for (int i = 0; i < xPositions.length; i++) {
75+
pRA.setPosition(xPositions[i], 0);
76+
pRA.setPosition(yPositions[i], 1);
77+
assertEquals(pvalueExpected[i], pRA.get().getRealDouble());
78+
}
79+
}
80+
}

0 commit comments

Comments
 (0)