Skip to content

Commit e037b90

Browse files
stelfrichawalter17
authored andcommitted
Add (P)SNR, RMSE, and MAE implementations
Adds various metrics for comparing reference images with (noisy) test images.
1 parent 1d1d6f7 commit e037b90

File tree

7 files changed

+556
-0
lines changed

7 files changed

+556
-0
lines changed

src/main/java/net/imagej/ops/image/ImageNamespace.java

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import net.imglib2.type.Type;
4343
import net.imglib2.type.numeric.RealType;
4444
import net.imglib2.type.numeric.integer.IntType;
45+
import net.imglib2.type.numeric.real.DoubleType;
4546

4647
import org.scijava.plugin.Plugin;
4748

@@ -456,6 +457,80 @@ <T extends RealType<T>> IterableInterval<T> normalize(
456457
return result;
457458
}
458459

460+
// -- quality --
461+
462+
@OpMethod(op = net.imagej.ops.image.quality.DefaultMAE.class)
463+
public <T extends RealType<T>> DoubleType mae(final DoubleType out,
464+
final IterableInterval<T> reference, final IterableInterval<T> test)
465+
{
466+
final DoubleType result = (DoubleType) ops().run(
467+
net.imagej.ops.image.quality.DefaultMAE.class, out, reference, test);
468+
return result;
469+
}
470+
471+
@OpMethod(op = net.imagej.ops.image.quality.DefaultMAE.class)
472+
public <T extends RealType<T>> DoubleType mae(
473+
final IterableInterval<T> reference, final IterableInterval<T> test)
474+
{
475+
final DoubleType result = (DoubleType) ops().run(
476+
net.imagej.ops.image.quality.DefaultMAE.class, reference, test);
477+
return result;
478+
}
479+
480+
@OpMethod(op = net.imagej.ops.image.quality.DefaultPSNR.class)
481+
public <T extends RealType<T>> DoubleType psnr(final DoubleType out,
482+
final IterableInterval<T> reference, final IterableInterval<T> test)
483+
{
484+
final DoubleType result = (DoubleType) ops().run(
485+
net.imagej.ops.image.quality.DefaultPSNR.class, out, reference, test);
486+
return result;
487+
}
488+
489+
@OpMethod(op = net.imagej.ops.image.quality.DefaultPSNR.class)
490+
public <T extends RealType<T>> DoubleType psnr(
491+
final IterableInterval<T> reference, final IterableInterval<T> test)
492+
{
493+
final DoubleType result = (DoubleType) ops().run(
494+
net.imagej.ops.image.quality.DefaultPSNR.class, reference, test);
495+
return result;
496+
}
497+
498+
@OpMethod(op = net.imagej.ops.image.quality.DefaultRMSE.class)
499+
public <T extends RealType<T>> DoubleType rmse(final DoubleType out,
500+
final IterableInterval<T> reference, final IterableInterval<T> test)
501+
{
502+
final DoubleType result = (DoubleType) ops().run(
503+
net.imagej.ops.image.quality.DefaultRMSE.class, out, reference, test);
504+
return result;
505+
}
506+
507+
@OpMethod(op = net.imagej.ops.image.quality.DefaultRMSE.class)
508+
public <T extends RealType<T>> DoubleType rmse(
509+
final IterableInterval<T> reference, final IterableInterval<T> test)
510+
{
511+
final DoubleType result = (DoubleType) ops().run(
512+
net.imagej.ops.image.quality.DefaultRMSE.class, reference, test);
513+
return result;
514+
}
515+
516+
@OpMethod(op = net.imagej.ops.image.quality.DefaultSNR.class)
517+
public <T extends RealType<T>> DoubleType snr(final DoubleType out,
518+
final IterableInterval<T> reference, final IterableInterval<T> test)
519+
{
520+
final DoubleType result = (DoubleType) ops().run(
521+
net.imagej.ops.image.quality.DefaultSNR.class, out, reference, test);
522+
return result;
523+
}
524+
525+
@OpMethod(op = net.imagej.ops.image.quality.DefaultSNR.class)
526+
public <T extends RealType<T>> DoubleType snr(
527+
final IterableInterval<T> reference, final IterableInterval<T> test)
528+
{
529+
final DoubleType result = (DoubleType) ops().run(
530+
net.imagej.ops.image.quality.DefaultSNR.class, reference, test);
531+
return result;
532+
}
533+
459534
// -- watershed --
460535

461536
/** Executes the "watershed" operation on the given arguments. */
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* #%L
3+
* ImageJ software for multidimensional image processing and analysis.
4+
* %%
5+
* Copyright (C) 2014 - 2018 ImageJ 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 net.imagej.ops.image.quality;
31+
32+
import net.imagej.ops.Contingent;
33+
import net.imagej.ops.Ops;
34+
import net.imagej.ops.map.Maps;
35+
import net.imagej.ops.special.hybrid.AbstractBinaryHybridCF;
36+
import net.imglib2.Cursor;
37+
import net.imglib2.IterableInterval;
38+
import net.imglib2.type.numeric.RealType;
39+
import net.imglib2.type.numeric.real.DoubleType;
40+
import net.imglib2.util.Intervals;
41+
42+
import org.scijava.plugin.Plugin;
43+
44+
/**
45+
* Computes the mean absolute error (MAE) between a reference image and a
46+
* (noisy) test image.
47+
* <p>
48+
* Computations are based on the definitions of Gonzalez (R.C. Gonzalez and R.E.
49+
* Woods, "Digital Image Processing," Prentice Hall 2008).
50+
* </p>
51+
*
52+
* @author Stefan Helfrich (University of Konstanz)
53+
* @param <I> type of input elements
54+
*/
55+
@Plugin(type = Ops.Image.MAE.class)
56+
public class DefaultMAE<I extends RealType<I>> extends
57+
AbstractBinaryHybridCF<IterableInterval<I>, IterableInterval<I>, DoubleType>
58+
implements Ops.Image.MAE, Contingent
59+
{
60+
61+
@Override
62+
public void compute(final IterableInterval<I> input1,
63+
final IterableInterval<I> input2, final DoubleType output)
64+
{
65+
final Cursor<I> cursor = input1.cursor();
66+
final Cursor<I> cursor2 = input2.cursor();
67+
68+
double denominatorSum = 0d;
69+
while (cursor.hasNext()) {
70+
final double r = cursor.next().getRealDouble();
71+
final double t = cursor2.next().getRealDouble();
72+
final double abs = Math.abs(r - t);
73+
denominatorSum += abs;
74+
}
75+
76+
denominatorSum *= 1d / Intervals.numElements(input1);
77+
output.setReal(denominatorSum);
78+
}
79+
80+
@Override
81+
public boolean conforms() {
82+
return Intervals.equalDimensions(in1(), in2()) && //
83+
Maps.compatible(in1(), in2());
84+
}
85+
86+
@Override
87+
public DoubleType createOutput(final IterableInterval<I> input1,
88+
final IterableInterval<I> input2)
89+
{
90+
return new DoubleType();
91+
}
92+
93+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* #%L
3+
* ImageJ software for multidimensional image processing and analysis.
4+
* %%
5+
* Copyright (C) 2014 - 2018 ImageJ 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 net.imagej.ops.image.quality;
31+
32+
import net.imagej.ops.Contingent;
33+
import net.imagej.ops.OpService;
34+
import net.imagej.ops.Ops;
35+
import net.imagej.ops.map.Maps;
36+
import net.imagej.ops.special.hybrid.AbstractBinaryHybridCF;
37+
import net.imglib2.Cursor;
38+
import net.imglib2.IterableInterval;
39+
import net.imglib2.type.numeric.RealType;
40+
import net.imglib2.type.numeric.real.DoubleType;
41+
import net.imglib2.util.Intervals;
42+
43+
import org.scijava.plugin.Parameter;
44+
import org.scijava.plugin.Plugin;
45+
46+
/**
47+
* Computes peak signal-to-noise ratio (PSNR) between a reference image and a
48+
* (noisy) test image. The resulting PSNR is expressed in decibel.
49+
* <p>
50+
* Computations are based on the definitions of Gonzalez (R.C. Gonzalez and R.E.
51+
* Woods, "Digital Image Processing," Prentice Hall 2008).
52+
* </p>
53+
*
54+
* @author Stefan Helfrich (University of Konstanz)
55+
* @param <I> type of input elements
56+
*/
57+
@Plugin(type = Ops.Image.PSNR.class)
58+
public class DefaultPSNR<I extends RealType<I>> extends
59+
AbstractBinaryHybridCF<IterableInterval<I>, IterableInterval<I>, DoubleType>
60+
implements Ops.Image.PSNR, Contingent
61+
{
62+
63+
@Parameter
64+
private OpService opService;
65+
66+
@Override
67+
public void compute(final IterableInterval<I> input1,
68+
final IterableInterval<I> input2, final DoubleType output)
69+
{
70+
final Cursor<I> cursor = input1.cursor();
71+
final Cursor<I> cursor2 = input2.cursor();
72+
double max = opService.stats().max(input1).getRealDouble();
73+
max *= max;
74+
75+
double denominatorSum = 0d;
76+
while (cursor.hasNext()) {
77+
final double r = cursor.next().getRealDouble();
78+
final double t = cursor2.next().getRealDouble();
79+
denominatorSum += Math.pow(r - t, 2);
80+
}
81+
82+
denominatorSum *= 1d / Intervals.numElements(input1);
83+
final double psnr = 10 * Math.log10(max / denominatorSum);
84+
85+
output.setReal(psnr);
86+
}
87+
88+
@Override
89+
public boolean conforms() {
90+
return Intervals.equalDimensions(in1(), in2()) && //
91+
Maps.compatible(in1(), in2());
92+
}
93+
94+
@Override
95+
public DoubleType createOutput(final IterableInterval<I> input1,
96+
final IterableInterval<I> input2)
97+
{
98+
return new DoubleType();
99+
}
100+
101+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* #%L
3+
* ImageJ software for multidimensional image processing and analysis.
4+
* %%
5+
* Copyright (C) 2014 - 2018 ImageJ 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 net.imagej.ops.image.quality;
31+
32+
import net.imagej.ops.Contingent;
33+
import net.imagej.ops.Ops;
34+
import net.imagej.ops.map.Maps;
35+
import net.imagej.ops.special.hybrid.AbstractBinaryHybridCF;
36+
import net.imglib2.Cursor;
37+
import net.imglib2.IterableInterval;
38+
import net.imglib2.type.numeric.RealType;
39+
import net.imglib2.type.numeric.real.DoubleType;
40+
import net.imglib2.util.Intervals;
41+
42+
import org.scijava.plugin.Plugin;
43+
44+
/**
45+
* Computes the root mean square error (RMSE) between a reference image and a
46+
* (noisy) test image.
47+
* <p>
48+
* Computations are based on the definitions of Gonzalez (R.C. Gonzalez and R.E.
49+
* Woods, "Digital Image Processing," Prentice Hall 2008).
50+
* </p>
51+
*
52+
* @author Stefan Helfrich (University of Konstanz)
53+
* @param <I> type of input elements
54+
*/
55+
@Plugin(type = Ops.Image.RMSE.class)
56+
public class DefaultRMSE<I extends RealType<I>> extends
57+
AbstractBinaryHybridCF<IterableInterval<I>, IterableInterval<I>, DoubleType>
58+
implements Ops.Image.RMSE, Contingent
59+
{
60+
61+
@Override
62+
public void compute(final IterableInterval<I> input1,
63+
final IterableInterval<I> input2, final DoubleType output)
64+
{
65+
final Cursor<I> cursor = input1.cursor();
66+
final Cursor<I> cursor2 = input2.cursor();
67+
68+
double denominatorSum = 0d;
69+
while (cursor.hasNext()) {
70+
final double r = cursor.next().getRealDouble();
71+
final double t = cursor2.next().getRealDouble();
72+
denominatorSum += Math.pow(r - t, 2);
73+
}
74+
75+
denominatorSum *= 1d / Intervals.numElements(input1);
76+
77+
final double rmse = Math.sqrt(denominatorSum);
78+
79+
output.setReal(rmse);
80+
}
81+
82+
@Override
83+
public boolean conforms() {
84+
return Intervals.equalDimensions(in1(), in2()) && //
85+
Maps.compatible(in1(), in2());
86+
}
87+
88+
@Override
89+
public DoubleType createOutput(final IterableInterval<I> input1,
90+
final IterableInterval<I> input2)
91+
{
92+
return new DoubleType();
93+
}
94+
95+
}

0 commit comments

Comments
 (0)