Skip to content

Commit c516821

Browse files
authored
Merge pull request #18 from qupath/more-accurate-interpolation
More accurate interpolation
2 parents 998c4dd + 2549cc4 commit c516821

7 files changed

Lines changed: 289 additions & 29 deletions

File tree

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ plugins {
44
}
55

66
group = "io.github.qupath"
7-
version = "0.1.0"
7+
version = "0.1.1"
88

99
repositories {
1010
mavenCentral()

src/main/java/qupath/ext/imglib2/AccessibleScaler.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
import net.imglib2.RandomAccessible;
44
import net.imglib2.RandomAccessibleInterval;
55
import net.imglib2.interpolation.InterpolatorFactory;
6-
import net.imglib2.interpolation.randomaccess.NLinearInterpolatorFactory;
76
import net.imglib2.interpolation.randomaccess.NearestNeighborInterpolatorFactory;
87
import net.imglib2.realtransform.AffineGet;
98
import net.imglib2.realtransform.RealViews;
109
import net.imglib2.realtransform.Scale2D;
1110
import net.imglib2.realtransform.Translation2D;
1211
import net.imglib2.type.numeric.NumericType;
1312
import net.imglib2.view.Views;
13+
import qupath.ext.imglib2.interpolators.LinearInterpolationFactory;
1414

1515
import java.util.Arrays;
1616
import java.util.stream.LongStream;
@@ -35,15 +35,17 @@ private AccessibleScaler() {
3535
* @param scale the scale to apply to the first two dimensions of the input {@link RandomAccessibleInterval}. Shouldn't be
3636
* less than or equal to 0
3737
* @return the input if the provided scale is 1, or a new scaled {@link RandomAccessibleInterval} otherwise
38-
* @param <T> the type of elements of the random accessible interval
38+
* @param <T> the type of elements of the random accessible interval. It must be {@link net.imglib2.type.numeric.ARGBType}
39+
* or an instance of {@link net.imglib2.type.numeric.RealType}, otherwise a {@link IllegalArgumentException}
40+
* will be thrown when accessing the output
3941
* @throws IllegalArgumentException if the input interval has at least one minimum different from 0, if the provided scale is less
4042
* than or equal to 0, or if the input interval has less than two dimensions
4143
*/
4244
public static <T extends NumericType<T>> RandomAccessibleInterval<T> scaleWithLinearInterpolation(
4345
RandomAccessibleInterval<T> input,
4446
double scale
4547
) {
46-
return scale(input, scale, new NLinearInterpolatorFactory<>());
48+
return scale(input, scale, new LinearInterpolationFactory<>());
4749
}
4850

4951
/**
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package qupath.ext.imglib2.interpolators;
2+
3+
import net.imglib2.RandomAccessible;
4+
import net.imglib2.interpolation.randomaccess.NLinearInterpolator2D;
5+
import net.imglib2.type.numeric.ARGBType;
6+
7+
/**
8+
* A {@link NLinearInterpolator2D} that performs intermediary operations with doubles, to avoid precision errors.
9+
*/
10+
class ArgbLinearInterpolator extends NLinearInterpolator2D<ARGBType> {
11+
12+
/**
13+
* Create an instance of this class.
14+
*
15+
* @param randomAccessible the accessible to interpolate
16+
*/
17+
public ArgbLinearInterpolator(RandomAccessible<ARGBType> randomAccessible) {
18+
super(randomAccessible);
19+
}
20+
21+
@Override
22+
public ARGBType get()
23+
{
24+
fillWeights();
25+
26+
int pixelValue;
27+
double red = 0;
28+
double green = 0;
29+
double blue = 0;
30+
double alpha = 0;
31+
32+
pixelValue = target.get().get();
33+
red += ARGBType.red(pixelValue) * weights[0];
34+
green += ARGBType.green(pixelValue) * weights[0];
35+
blue += ARGBType.blue(pixelValue) * weights[0];
36+
alpha += ARGBType.alpha(pixelValue) * weights[0];
37+
38+
target.fwd(0);
39+
pixelValue = target.get().get();
40+
red += ARGBType.red(pixelValue) * weights[1];
41+
green += ARGBType.green(pixelValue) * weights[1];
42+
blue += ARGBType.blue(pixelValue) * weights[1];
43+
alpha += ARGBType.alpha(pixelValue) * weights[1];
44+
45+
target.fwd(1);
46+
pixelValue = target.get().get();
47+
red += ARGBType.red(pixelValue) * weights[3];
48+
green += ARGBType.green(pixelValue) * weights[3];
49+
blue += ARGBType.blue(pixelValue) * weights[3];
50+
alpha += ARGBType.alpha(pixelValue) * weights[3];
51+
52+
target.bck(0);
53+
pixelValue = target.get().get();
54+
red += ARGBType.red(pixelValue) * weights[2];
55+
green += ARGBType.green(pixelValue) * weights[2];
56+
blue += ARGBType.blue(pixelValue) * weights[2];
57+
alpha += ARGBType.alpha(pixelValue) * weights[2];
58+
59+
target.bck(1);
60+
61+
accumulator.set(ARGBType.rgba(red, green, blue, alpha));
62+
63+
return accumulator;
64+
}
65+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package qupath.ext.imglib2.interpolators;
2+
3+
import net.imglib2.RandomAccessible;
4+
import net.imglib2.RealInterval;
5+
import net.imglib2.RealRandomAccess;
6+
import net.imglib2.interpolation.InterpolatorFactory;
7+
import net.imglib2.type.numeric.ARGBType;
8+
import net.imglib2.type.numeric.NumericType;
9+
import net.imglib2.type.numeric.RealType;
10+
11+
/**
12+
* An {@link InterpolatorFactory} that implements a 2D linear interpolation.
13+
* <p>
14+
* This factory can only work with {@link RandomAccessible} that have 2 dimensions and the {@link ARGBType} or {@link RealType}
15+
* type. If {@link #create(RandomAccessible)} is called with an invalid {@link RandomAccessible}, an {@link IllegalArgumentException}
16+
* is thrown.
17+
*
18+
* @param <T> the type of {@link RandomAccessible} to interpolate
19+
*/
20+
public class LinearInterpolationFactory<T extends NumericType<T>> implements InterpolatorFactory<T, RandomAccessible<T>> {
21+
22+
@Override
23+
@SuppressWarnings({"unchecked", "rawtypes"})
24+
public RealRandomAccess<T> create(RandomAccessible randomAccessible) {
25+
if (randomAccessible.numDimensions() != 2) {
26+
throw new IllegalArgumentException(String.format(
27+
"The provided accessible has not 2 dimensions (found %d)",
28+
randomAccessible.numDimensions()
29+
));
30+
}
31+
32+
Object type = randomAccessible.randomAccess().get();
33+
if (type instanceof ARGBType) {
34+
return (RealRandomAccess<T>) new ArgbLinearInterpolator(randomAccessible);
35+
} else if (type instanceof RealType<?>) {
36+
return (RealRandomAccess<T>) new RealLinearInterpolator<>(randomAccessible);
37+
} else {
38+
throw new IllegalArgumentException(String.format(
39+
"The type of the provided accessible %s is unexpected",
40+
type
41+
));
42+
}
43+
}
44+
45+
@Override
46+
public RealRandomAccess<T> create(RandomAccessible<T> randomAccessible, RealInterval interval) {
47+
return create(randomAccessible);
48+
}
49+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package qupath.ext.imglib2.interpolators;
2+
3+
import net.imglib2.RandomAccessible;
4+
import net.imglib2.interpolation.randomaccess.NLinearInterpolator2D;
5+
import net.imglib2.type.numeric.RealType;
6+
7+
/**
8+
* A {@link NLinearInterpolator2D} that performs intermediary operations with doubles, to avoid precision errors.
9+
*/
10+
class RealLinearInterpolator<T extends RealType<T>> extends NLinearInterpolator2D<T> {
11+
12+
/**
13+
* Create an instance of this class.
14+
*
15+
* @param randomAccessible the accessible to interpolate
16+
*/
17+
public RealLinearInterpolator(RandomAccessible<T> randomAccessible) {
18+
super(randomAccessible);
19+
}
20+
21+
@Override
22+
public T get()
23+
{
24+
fillWeights();
25+
26+
double value = target.get().getRealDouble() * weights[0];
27+
28+
target.fwd(0);
29+
value += target.get().getRealDouble() * weights[1];
30+
31+
target.fwd(1);
32+
value += target.get().getRealDouble() * weights[3];
33+
34+
target.bck(0);
35+
value += target.get().getRealDouble() * weights[2];
36+
37+
target.bck(1);
38+
39+
accumulator.setReal(value);
40+
41+
return accumulator;
42+
}
43+
}

0 commit comments

Comments
 (0)