Skip to content

Commit 61119d0

Browse files
committed
Add (P)SNR, RMSE, and MAE implementations
Adds various metrics for comparing reference images with (noisy) test images.
1 parent feeb13e commit 61119d0

File tree

7 files changed

+559
-2
lines changed

7 files changed

+559
-2
lines changed

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

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@
4242
import net.imglib2.type.Type;
4343
import net.imglib2.type.numeric.IntegerType;
4444
import net.imglib2.type.numeric.RealType;
45+
<<<<<<< feeb13e88b0439bc34b78168a232e062dc5be692
4546
import net.imglib2.type.numeric.integer.IntType;
47+
=======
48+
import net.imglib2.type.numeric.real.DoubleType;
49+
>>>>>>> Add (P)SNR, RMSE, and MAE implementations
4650

4751
import org.scijava.plugin.Plugin;
4852

@@ -469,6 +473,80 @@ <T extends RealType<T>> IterableInterval<T> normalize(
469473
return result;
470474
}
471475

476+
// -- quality --
477+
478+
@OpMethod(op = net.imagej.ops.image.quality.DefaultMAE.class)
479+
public <T extends RealType<T>> DoubleType mae(final DoubleType out,
480+
final IterableInterval<T> reference, final IterableInterval<T> test)
481+
{
482+
final DoubleType result = (DoubleType) ops().run(
483+
net.imagej.ops.image.quality.DefaultMAE.class, out, reference, test);
484+
return result;
485+
}
486+
487+
@OpMethod(op = net.imagej.ops.image.quality.DefaultMAE.class)
488+
public <T extends RealType<T>> DoubleType mae(
489+
final IterableInterval<T> reference, final IterableInterval<T> test)
490+
{
491+
final DoubleType result = (DoubleType) ops().run(
492+
net.imagej.ops.image.quality.DefaultMAE.class, reference, test);
493+
return result;
494+
}
495+
496+
@OpMethod(op = net.imagej.ops.image.quality.DefaultPSNR.class)
497+
public <T extends RealType<T>> DoubleType psnr(final DoubleType out,
498+
final IterableInterval<T> reference, final IterableInterval<T> test)
499+
{
500+
final DoubleType result = (DoubleType) ops().run(
501+
net.imagej.ops.image.quality.DefaultPSNR.class, out, reference, test);
502+
return result;
503+
}
504+
505+
@OpMethod(op = net.imagej.ops.image.quality.DefaultPSNR.class)
506+
public <T extends RealType<T>> DoubleType psnr(
507+
final IterableInterval<T> reference, final IterableInterval<T> test)
508+
{
509+
final DoubleType result = (DoubleType) ops().run(
510+
net.imagej.ops.image.quality.DefaultPSNR.class, reference, test);
511+
return result;
512+
}
513+
514+
@OpMethod(op = net.imagej.ops.image.quality.DefaultRMSE.class)
515+
public <T extends RealType<T>> DoubleType rmse(final DoubleType out,
516+
final IterableInterval<T> reference, final IterableInterval<T> test)
517+
{
518+
final DoubleType result = (DoubleType) ops().run(
519+
net.imagej.ops.image.quality.DefaultRMSE.class, out, reference, test);
520+
return result;
521+
}
522+
523+
@OpMethod(op = net.imagej.ops.image.quality.DefaultRMSE.class)
524+
public <T extends RealType<T>> DoubleType rmse(
525+
final IterableInterval<T> reference, final IterableInterval<T> test)
526+
{
527+
final DoubleType result = (DoubleType) ops().run(
528+
net.imagej.ops.image.quality.DefaultRMSE.class, reference, test);
529+
return result;
530+
}
531+
532+
@OpMethod(op = net.imagej.ops.image.quality.DefaultSNR.class)
533+
public <T extends RealType<T>> DoubleType snr(final DoubleType out,
534+
final IterableInterval<T> reference, final IterableInterval<T> test)
535+
{
536+
final DoubleType result = (DoubleType) ops().run(
537+
net.imagej.ops.image.quality.DefaultSNR.class, out, reference, test);
538+
return result;
539+
}
540+
541+
@OpMethod(op = net.imagej.ops.image.quality.DefaultSNR.class)
542+
public <T extends RealType<T>> DoubleType snr(
543+
final IterableInterval<T> reference, final IterableInterval<T> test)
544+
{
545+
final DoubleType result = (DoubleType) ops().run(
546+
net.imagej.ops.image.quality.DefaultSNR.class, reference, test);
547+
return result;
548+
}
549+
472550
// -- watershed --
473551

474552
/** Executes the "watershed" operation on the given arguments. */
@@ -609,8 +687,6 @@ public <B extends BooleanType<B>, T extends RealType<T>> ImgLabeling<Integer, In
609687
final ImgLabeling<Integer, IntType> result = (ImgLabeling<Integer, IntType>) ops().run(
610688
net.imagej.ops.image.watershed.WatershedSeeded.class, out, in, seeds, eightConnectivity, drawWatersheds,
611689
mask);
612-
return result;
613-
}
614690

615691
// -- Named methods --
616692

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 - 2017 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+
Cursor<I> cursor = input1.cursor();
66+
Cursor<I> cursor2 = input2.cursor();
67+
68+
double denominatorSum = 0d;
69+
while (cursor.hasNext()) {
70+
double r = cursor.next().getRealDouble();
71+
double t = cursor2.next().getRealDouble();
72+
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(IterableInterval<I> input1,
88+
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 - 2017 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+
Cursor<I> cursor = input1.cursor();
71+
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+
double r = cursor.next().getRealDouble();
78+
double t = cursor2.next().getRealDouble();
79+
denominatorSum += Math.pow(r - t, 2);
80+
}
81+
82+
denominatorSum *= 1d / Intervals.numElements(input1);
83+
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(IterableInterval<I> input1,
96+
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 - 2017 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+
Cursor<I> cursor = input1.cursor();
66+
Cursor<I> cursor2 = input2.cursor();
67+
68+
double denominatorSum = 0d;
69+
while (cursor.hasNext()) {
70+
double r = cursor.next().getRealDouble();
71+
double t = cursor2.next().getRealDouble();
72+
denominatorSum += Math.pow(r - t, 2);
73+
}
74+
75+
denominatorSum *= 1d / Intervals.numElements(input1);
76+
77+
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(IterableInterval<I> input1,
90+
IterableInterval<I> input2)
91+
{
92+
return new DoubleType();
93+
}
94+
95+
}

0 commit comments

Comments
 (0)