Skip to content

Commit cbebe43

Browse files
committed
Add DefaultSmooth and DefaultSharpen Ops
Adapted from the implementations created by Barry Dezonia
1 parent e8b7cb5 commit cbebe43

File tree

4 files changed

+295
-4
lines changed

4 files changed

+295
-4
lines changed

src/main/java/net/imagej/ops/filter/FilterNamespace.java

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
* %%
77
* Redistribution and use in source and binary forms, with or without
88
* modification, are permitted provided that the following conditions are met:
9-
*
9+
*
1010
* 1. Redistributions of source code must retain the above copyright notice,
1111
* this list of conditions and the following disclaimer.
1212
* 2. Redistributions in binary form must reproduce the above copyright notice,
1313
* this list of conditions and the following disclaimer in the documentation
1414
* and/or other materials provided with the distribution.
15-
*
15+
*
1616
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1717
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1818
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
@@ -136,7 +136,7 @@ public <I extends RealType<I>, O extends RealType<O>> O addPoissonNoise(
136136

137137
/**
138138
* Executes a bilateral filter on the given arguments.
139-
*
139+
*
140140
* @param in
141141
* @param out
142142
* @param sigmaR
@@ -757,7 +757,7 @@ RandomAccessibleInterval<V> dog(final RandomAccessibleInterval<T> in,
757757

758758
/**
759759
* Executes the "Frangi Vesselness" filter operation on the given arguments.
760-
*
760+
*
761761
* @param in - input image
762762
* @param out - output image
763763
* @param spacing - n-dimensional array indicating the physical distance
@@ -1245,6 +1245,19 @@ public <T extends RealType<T>> RandomAccessibleInterval<T> partialDerivative(
12451245
return result;
12461246
}
12471247

1248+
// -- Sharpen
1249+
1250+
/** Executes the "sharpen" filter operation on the given arguments. */
1251+
@OpMethod(op = net.imagej.ops.filter.sharpen.DefaultSharpen.class)
1252+
public <T extends RealType<T>> RandomAccessibleInterval<T> sharpen(
1253+
final RandomAccessibleInterval<T> in)
1254+
{
1255+
@SuppressWarnings("unchecked")
1256+
final RandomAccessibleInterval<T> result =
1257+
(RandomAccessibleInterval<T>) ops().run(Ops.Filter.Sharpen.class, in);
1258+
return result;
1259+
}
1260+
12481261
/** Executes the "sigma" filter operation on the given arguments. */
12491262
@OpMethod(op = net.imagej.ops.filter.sigma.DefaultSigmaFilter.class)
12501263
public <T extends RealType<T>> IterableInterval<T> sigma(
@@ -1271,6 +1284,19 @@ public <T extends RealType<T>> IterableInterval<T> sigma(
12711284
return result;
12721285
}
12731286

1287+
// -- Smooth
1288+
1289+
/** Executes the "sharpen" filter operation on the given arguments. */
1290+
@OpMethod(op = net.imagej.ops.filter.smooth.DefaultSmooth.class)
1291+
public <T extends RealType<T>> RandomAccessibleInterval<T> smooth(
1292+
final RandomAccessibleInterval<T> in)
1293+
{
1294+
@SuppressWarnings("unchecked")
1295+
final RandomAccessibleInterval<T> result =
1296+
(RandomAccessibleInterval<T>) ops().run(Ops.Filter.Sharpen.class, in);
1297+
return result;
1298+
}
1299+
12741300
// -- Sobel
12751301

12761302
@OpMethod(op = net.imagej.ops.filter.sobel.SobelRAI.class)
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
2+
package net.imagej.ops.filter.sharpen;
3+
4+
import net.imagej.Extents;
5+
import net.imagej.Position;
6+
import net.imagej.ops.Ops;
7+
import net.imagej.ops.special.function.AbstractUnaryFunctionOp;
8+
import net.imglib2.Cursor;
9+
import net.imglib2.FinalInterval;
10+
import net.imglib2.RandomAccess;
11+
import net.imglib2.RandomAccessible;
12+
import net.imglib2.RandomAccessibleInterval;
13+
import net.imglib2.algorithm.neighborhood.Neighborhood;
14+
import net.imglib2.algorithm.neighborhood.RectangleNeighborhood;
15+
import net.imglib2.algorithm.neighborhood.RectangleNeighborhoodFactory;
16+
import net.imglib2.algorithm.neighborhood.RectangleShape.NeighborhoodsAccessible;
17+
import net.imglib2.type.numeric.RealType;
18+
import net.imglib2.util.Util;
19+
import net.imglib2.view.Views;
20+
21+
import org.scijava.plugin.Plugin;
22+
23+
/**
24+
* Op rendition of the SharpenDataValues plugin written by Barry Dezonia
25+
*
26+
* @author Gabe Selzer
27+
*/
28+
@Plugin(type = Ops.Filter.Sharpen.class)
29+
public class DefaultSharpen<T extends RealType<T>> extends
30+
AbstractUnaryFunctionOp<RandomAccessibleInterval<T>, RandomAccessibleInterval<T>>
31+
implements Ops.Filter.Sharpen
32+
{
33+
34+
final double[] kernel = { -1, -1, -1, -1, 12, -1, -1, -1, -1 };
35+
double scale;
36+
37+
@Override
38+
public RandomAccessibleInterval<T> calculate(
39+
final RandomAccessibleInterval<T> input)
40+
{
41+
final RandomAccessibleInterval<T> output = ops().copy().rai(input);
42+
43+
final long[] planeDims = new long[input.numDimensions() - 2];
44+
for (int i = 0; i < planeDims.length; i++)
45+
planeDims[i] = input.dimension(i + 2);
46+
final Extents extents = new Extents(planeDims);
47+
final Position planePos = extents.createPosition();
48+
if (planeDims.length == 0) {
49+
computePlanar(planePos, input, output);
50+
}
51+
else {
52+
while (planePos.hasNext()) {
53+
planePos.fwd();
54+
computePlanar(planePos, input, output);
55+
}
56+
57+
}
58+
return output;
59+
}
60+
61+
private void computePlanar(final Position planePos,
62+
final RandomAccessibleInterval<T> input,
63+
final RandomAccessibleInterval<T> output)
64+
{
65+
// TODO can we just set scale to 4?
66+
scale = 0;
67+
for (final double d : kernel)
68+
scale += d;
69+
70+
final T type = Util.getTypeFromInterval(input);
71+
72+
final long[] imageDims = new long[input.numDimensions()];
73+
input.dimensions(imageDims);
74+
75+
// create all objects needed for NeighborhoodsAccessible
76+
RandomAccessibleInterval<T> slicedInput = ops().copy().rai(input);
77+
for (int i = planePos.numDimensions() - 1; i >= 0; i--) {
78+
slicedInput = Views.hyperSlice(slicedInput, input.numDimensions() - 1 - i,
79+
planePos.getLongPosition(i));
80+
}
81+
82+
final RandomAccessible<T> refactoredInput = Views.extendMirrorSingle(
83+
slicedInput);
84+
final RectangleNeighborhoodFactory<T> factory = RectangleNeighborhood
85+
.factory();
86+
final FinalInterval neighborhoodSpan = new FinalInterval(new long[] { -1,
87+
-1 }, new long[] { 1, 1 });
88+
89+
final NeighborhoodsAccessible<T> neighborhoods =
90+
new NeighborhoodsAccessible<>(refactoredInput, neighborhoodSpan, factory);
91+
92+
// create cursors and random accesses for loop.
93+
final Cursor<T> cursor = Views.iterable(input).localizingCursor();
94+
final RandomAccess<T> outputRA = output.randomAccess();
95+
for (int i = 0; i < planePos.numDimensions(); i++) {
96+
outputRA.setPosition(planePos.getLongPosition(i), i + 2);
97+
}
98+
final RandomAccess<Neighborhood<T>> neighborhoodsRA = neighborhoods
99+
.randomAccess();
100+
101+
int algorithmIndex = 0;
102+
double sum;
103+
final double[] n = new double[9];
104+
while (cursor.hasNext()) {
105+
cursor.fwd();
106+
if (cursor.getLongPosition(0) == 14 && cursor.getLongPosition(1) == 0)
107+
System.out.println("Hit 14");
108+
neighborhoodsRA.setPosition(cursor);
109+
final Neighborhood<T> current = neighborhoodsRA.get();
110+
final Cursor<T> neighborhoodCursor = current.cursor();
111+
112+
algorithmIndex = 0;
113+
sum = 0;
114+
while (algorithmIndex < n.length) {
115+
neighborhoodCursor.fwd();
116+
n[algorithmIndex++] = neighborhoodCursor.get().getRealDouble();
117+
}
118+
119+
for (int i = 0; i < kernel.length; i++) {
120+
sum += kernel[i] * n[i];
121+
}
122+
123+
double value = sum / scale;
124+
125+
outputRA.setPosition(cursor.getLongPosition(0), 0);
126+
outputRA.setPosition(cursor.getLongPosition(1), 1);
127+
if (value > type.getMaxValue()) value = type.getMaxValue();
128+
if (value < type.getMinValue()) value = type.getMinValue();
129+
outputRA.get().setReal(value);
130+
}
131+
}
132+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
2+
package net.imagej.ops.filter.smooth;
3+
4+
import net.imagej.Extents;
5+
import net.imagej.Position;
6+
import net.imagej.ops.Ops;
7+
import net.imagej.ops.special.function.AbstractUnaryFunctionOp;
8+
import net.imglib2.Cursor;
9+
import net.imglib2.FinalInterval;
10+
import net.imglib2.RandomAccess;
11+
import net.imglib2.RandomAccessible;
12+
import net.imglib2.RandomAccessibleInterval;
13+
import net.imglib2.algorithm.neighborhood.Neighborhood;
14+
import net.imglib2.algorithm.neighborhood.RectangleNeighborhood;
15+
import net.imglib2.algorithm.neighborhood.RectangleNeighborhoodFactory;
16+
import net.imglib2.algorithm.neighborhood.RectangleShape.NeighborhoodsAccessible;
17+
import net.imglib2.type.numeric.RealType;
18+
import net.imglib2.util.Util;
19+
import net.imglib2.view.Views;
20+
21+
import org.scijava.plugin.Plugin;
22+
23+
/**
24+
* Op rendition of the SmoothDataValues plugin written by Barry Dezonia
25+
*
26+
* @author Gabe Selzer
27+
*/
28+
@Plugin(type = Ops.Filter.Smooth.class)
29+
public class DefaultSmooth<T extends RealType<T>> extends
30+
AbstractUnaryFunctionOp<RandomAccessibleInterval<T>, RandomAccessibleInterval<T>>
31+
implements Ops.Filter.Smooth
32+
{
33+
34+
final double[] kernel = { 1, 1, 1, 1, 1, 1, 1, 1, 1 };
35+
double scale;
36+
37+
@Override
38+
public RandomAccessibleInterval<T> calculate(
39+
final RandomAccessibleInterval<T> input)
40+
{
41+
final RandomAccessibleInterval<T> output = ops().copy().rai(input);
42+
43+
final long[] planeDims = new long[input.numDimensions() - 2];
44+
for (int i = 0; i < planeDims.length; i++)
45+
planeDims[i] = input.dimension(i + 2);
46+
final Extents extents = new Extents(planeDims);
47+
final Position planePos = extents.createPosition();
48+
if (planeDims.length == 0) {
49+
computePlanar(planePos, input, output);
50+
}
51+
else {
52+
while (planePos.hasNext()) {
53+
planePos.fwd();
54+
computePlanar(planePos, input, output);
55+
}
56+
57+
}
58+
return output;
59+
}
60+
61+
private void computePlanar(final Position planePos,
62+
final RandomAccessibleInterval<T> input,
63+
final RandomAccessibleInterval<T> output)
64+
{
65+
// TODO can we just set scale to 4?
66+
scale = 0;
67+
for (final double d : kernel)
68+
scale += d;
69+
70+
final T type = Util.getTypeFromInterval(input);
71+
72+
final long[] imageDims = new long[input.numDimensions()];
73+
input.dimensions(imageDims);
74+
75+
// create all objects needed for NeighborhoodsAccessible
76+
RandomAccessibleInterval<T> slicedInput = ops().copy().rai(input);
77+
for (int i = planePos.numDimensions() - 1; i >= 0; i--) {
78+
slicedInput = Views.hyperSlice(slicedInput, input.numDimensions() - 1 - i,
79+
planePos.getLongPosition(i));
80+
}
81+
82+
final RandomAccessible<T> refactoredInput = Views.extendMirrorSingle(
83+
slicedInput);
84+
final RectangleNeighborhoodFactory<T> factory = RectangleNeighborhood
85+
.factory();
86+
final FinalInterval neighborhoodSpan = new FinalInterval(new long[] { -1,
87+
-1 }, new long[] { 1, 1 });
88+
89+
final NeighborhoodsAccessible<T> neighborhoods =
90+
new NeighborhoodsAccessible<>(refactoredInput, neighborhoodSpan, factory);
91+
92+
// create cursors and random accesses for loop.
93+
final Cursor<T> cursor = Views.iterable(input).localizingCursor();
94+
final RandomAccess<T> outputRA = output.randomAccess();
95+
for (int i = 0; i < planePos.numDimensions(); i++) {
96+
outputRA.setPosition(planePos.getLongPosition(i), i + 2);
97+
}
98+
final RandomAccess<Neighborhood<T>> neighborhoodsRA = neighborhoods
99+
.randomAccess();
100+
101+
int algorithmIndex = 0;
102+
double sum;
103+
final double[] n = new double[9];
104+
while (cursor.hasNext()) {
105+
106+
cursor.fwd();
107+
neighborhoodsRA.setPosition(cursor);
108+
final Neighborhood<T> current = neighborhoodsRA.get();
109+
final Cursor<T> neighborhoodCursor = current.cursor();
110+
111+
algorithmIndex = 0;
112+
sum = 0;
113+
while (algorithmIndex < n.length) {
114+
neighborhoodCursor.fwd();
115+
n[algorithmIndex++] = neighborhoodCursor.get().getRealDouble();
116+
}
117+
118+
for (int i = 0; i < kernel.length; i++) {
119+
sum += kernel[i] * n[i];
120+
}
121+
122+
double value = sum / scale;
123+
124+
outputRA.setPosition(cursor.getLongPosition(0), 0);
125+
outputRA.setPosition(cursor.getLongPosition(1), 1);
126+
if (value > type.getMaxValue()) value = type.getMaxValue();
127+
if (value < type.getMinValue()) value = type.getMinValue();
128+
outputRA.get().setReal(value);
129+
}
130+
}
131+
}

src/main/templates/net/imagej/ops/Ops.list

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,9 @@ namespaces = ```
122122
[name: "paddingIntervalCentered", iface: "PaddingIntervalCentered"],
123123
[name: "paddingIntervalOrigin", iface: "PaddingIntervalOrigin"],
124124
[name: "tubeness", iface: "Tubeness"],
125+
[name: "sharpen", iface: "Sharpen"],
125126
[name: "sigma", iface: "Sigma", aliases: ["sigmaFilter", "filterSigma"]],
127+
[name: "smooth", iface: "Smooth"],
126128
[name: "sobel", iface: "Sobel"],
127129
[name: "variance", iface: "Variance", aliases: ["varianceFilter", "filterVariance", "var", "varFilter", "filterVar"]],
128130
[name: "frangiVesselness", iface: "FrangiVesselness"],

0 commit comments

Comments
 (0)