This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
commit c0ad361ea2c27195e5681162b4865372cb7fb46d
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Sun Apr 5 19:25:24 2020 +0200
Add more interpolation tests. Contains a correction about the size of ResampledImage tiles.
---
.../java/org/apache/sis/image/ResampledImage.java | 2 +-
.../coverage/j2d/BandedSampleConverter.java | 2 +-
.../sis/internal/coverage/j2d/ImageLayout.java | 37 ++++---
.../org/apache/sis/image/ResampledImageTest.java | 107 +++++++++++++++++++--
4 files changed, 125 insertions(+), 23 deletions(-)
diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/ResampledImage.java b/core/sis-feature/src/main/java/org/apache/sis/image/ResampledImage.java
index f9f1b05..b181a96 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/ResampledImage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/ResampledImage.java
@@ -132,7 +132,7 @@ public class ResampledImage extends ComputedImage {
protected ResampledImage(final Rectangle bounds, final MathTransform toSource, final
RenderedImage source,
final Interpolation interpolation, final Number[] fillValues)
{
- super(ImageLayout.DEFAULT.createCompatibleSampleModel(source), source);
+ super(ImageLayout.DEFAULT.createCompatibleSampleModel(source, bounds), source);
ArgumentChecks.ensureNonNull("interpolation", interpolation);
ArgumentChecks.ensureStrictlyPositive("width", width = bounds.width);
ArgumentChecks.ensureStrictlyPositive("height", height = bounds.height);
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BandedSampleConverter.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BandedSampleConverter.java
index c890cd1..156212c 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BandedSampleConverter.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BandedSampleConverter.java
@@ -162,7 +162,7 @@ public class BandedSampleConverter extends ComputedImage {
if (layout == null) {
layout = ImageLayout.DEFAULT;
}
- final Dimension tile = layout.suggestTileSize(source);
+ final Dimension tile = layout.suggestTileSize(source, null);
final BandedSampleModel sampleModel = RasterFactory.unique(
new BandedSampleModel(targetType, tile.width, tile.height, numBands));
/*
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageLayout.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageLayout.java
index 2cf3ce4..ae0da95 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageLayout.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageLayout.java
@@ -18,6 +18,7 @@ package org.apache.sis.internal.coverage.j2d;
import java.util.Arrays;
import java.awt.Dimension;
+import java.awt.Rectangle;
import java.awt.image.ColorModel;
import java.awt.image.IndexColorModel;
import java.awt.image.RenderedImage;
@@ -177,15 +178,13 @@ public class ImageLayout {
* <p>This method also checks whether the color model supports transparency. If
not, then this
* method will not return a size that may result in the creation of partially empty tiles.</p>
*
- * @param image the image for which to derive a tile size, or {@code null}.
+ * @param image the image for which to derive a tile size, or {@code null}.
+ * @param bounds the bounds of the image to create, or {@code null} is same as {@code
image}.
* @return suggested tile size for the given image.
*/
- public Dimension suggestTileSize(final RenderedImage image) {
- if (image == null) {
- return new Dimension(preferredTileWidth, preferredTileHeight);
- }
+ public Dimension suggestTileSize(final RenderedImage image, final Rectangle bounds) {
boolean pt = allowPartialTiles;
- if (pt) {
+ if (pt && image != null) {
final ColorModel cm = image.getColorModel();
if (pt = (cm != null)) {
if (cm instanceof IndexColorModel) {
@@ -201,10 +200,21 @@ public class ImageLayout {
* operations may assume that a call to `source.getTile(…)` will return a tile
covering fully the
* tile to compute.
*/
- final boolean singleXTile = image.getNumXTiles() <= 1;
- final boolean singleYTile = image.getNumYTiles() <= 1;
- int width = singleXTile ? image.getWidth() : image.getTileWidth();
- int height = singleYTile ? image.getHeight() : image.getTileHeight();
+ final boolean singleXTile, singleYTile;
+ final int width, height;
+ if (bounds != null) {
+ singleXTile = true;
+ singleYTile = true;
+ width = bounds.width;
+ height = bounds.height;
+ } else if (image != null) {
+ singleXTile = image.getNumXTiles() <= 1;
+ singleYTile = image.getNumYTiles() <= 1;
+ width = singleXTile ? image.getWidth() : image.getTileWidth();
+ height = singleYTile ? image.getHeight() : image.getTileHeight();
+ } else {
+ return new Dimension(preferredTileWidth, preferredTileHeight);
+ }
return new Dimension(toTileSize(width, preferredTileWidth, pt & singleXTile),
toTileSize(height, preferredTileHeight, pt & singleYTile));
}
@@ -215,14 +225,15 @@ public class ImageLayout {
* for determining the {@code sampleModel} argument of {@link ComputedImage}
* constructor.
*
- * @param image the image form which to get a sample model.
+ * @param image the image form which to get a sample model.
+ * @param bounds the bounds of the image to create, or {@code null} is same as {@code
image}.
* @return image sample model with preferred tile size.
*
* @see ComputedImage#ComputedImage(SampleModel, RenderedImage...)
*/
- public SampleModel createCompatibleSampleModel(final RenderedImage image) {
+ public SampleModel createCompatibleSampleModel(final RenderedImage image, final Rectangle
bounds) {
ArgumentChecks.ensureNonNull("image", image);
- final Dimension tile = suggestTileSize(image);
+ final Dimension tile = suggestTileSize(image, bounds);
SampleModel sm = image.getSampleModel();
if (sm.getWidth() != tile.width || sm.getHeight() != tile.height) {
sm = sm.createCompatibleSampleModel(tile.width, tile.height);
diff --git a/core/sis-feature/src/test/java/org/apache/sis/image/ResampledImageTest.java b/core/sis-feature/src/test/java/org/apache/sis/image/ResampledImageTest.java
index 5585cde..3b01c55 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/image/ResampledImageTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/image/ResampledImageTest.java
@@ -23,11 +23,15 @@ import java.awt.Point;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferFloat;
+import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import java.awt.image.BandedSampleModel;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import org.apache.sis.internal.coverage.j2d.TiledImage;
+import org.apache.sis.internal.coverage.j2d.RasterFactory;
import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
import org.apache.sis.test.TestCase;
import org.apache.sis.test.TestUtilities;
@@ -49,7 +53,7 @@ public final strictfp class ResampledImageTest extends TestCase {
* The source image. This is initialized to arbitrary values in two bands.
* Location and number of tiles are random.
*/
- private PlanarImage source;
+ private RenderedImage source;
/**
* The result of resampling {@link #source} image.
@@ -63,11 +67,11 @@ public final strictfp class ResampledImageTest extends TestCase {
private AffineTransform sourceToTarget;
/**
- * Creates a rendered image with arbitrary tiles.
+ * Creates a rendered image with arbitrary tiles and some random values.
*
* @param dataType {@link DataBuffer#TYPE_SHORT} or {@link DataBuffer#TYPE_FLOAT}.
*/
- private static PlanarImage createImage(final int dataType) {
+ private static PlanarImage createRandomImage(final int dataType) {
final Random random = TestUtilities.createRandomNumberGenerator();
final int tileWidth = random.nextInt(8) + 3;
final int tileHeight = random.nextInt(8) + 3;
@@ -189,7 +193,7 @@ public final strictfp class ResampledImageTest extends TestCase {
*/
@Test
public void testNearestOnFloats() throws NoninvertibleTransformException {
- source = createImage(DataBuffer.TYPE_FLOAT);
+ source = createRandomImage(DataBuffer.TYPE_FLOAT);
createScaledByTwo(Interpolation.BILINEAR, -30, 12);
verifyAtIntegerPositions();
}
@@ -201,7 +205,7 @@ public final strictfp class ResampledImageTest extends TestCase {
*/
@Test
public void testNearestOnIntegers() throws NoninvertibleTransformException {
- source = createImage(DataBuffer.TYPE_SHORT);
+ source = createRandomImage(DataBuffer.TYPE_SHORT);
createScaledByTwo(Interpolation.BILINEAR, 18, 20);
verifyAtIntegerPositions();
}
@@ -213,7 +217,7 @@ public final strictfp class ResampledImageTest extends TestCase {
*/
@Test
public void testBilinearOnFloats() throws NoninvertibleTransformException {
- source = createImage(DataBuffer.TYPE_FLOAT);
+ source = createRandomImage(DataBuffer.TYPE_FLOAT);
createScaledByTwo(Interpolation.BILINEAR, -40, 50);
verifyAtIntegerPositions();
verifyAtMiddlePositions(1E-12);
@@ -226,7 +230,7 @@ public final strictfp class ResampledImageTest extends TestCase {
*/
@Test
public void testBilinearOnIntegers() throws NoninvertibleTransformException {
- source = createImage(DataBuffer.TYPE_SHORT);
+ source = createRandomImage(DataBuffer.TYPE_SHORT);
createScaledByTwo(Interpolation.BILINEAR, 40, -50);
verifyAtIntegerPositions();
verifyAtMiddlePositions(0.5);
@@ -249,7 +253,7 @@ public final strictfp class ResampledImageTest extends TestCase {
}
}
source = new TiledImage(null, 3, 3, 0, 0, raster);
- assertNull(source.verify());
+ assertNull(((TiledImage) source).verify());
createScaledByTwo(Interpolation.BILINEAR, -2, -2);
final PixelIterator pt = PixelIterator.create(target);
assertResultEquals(pt, -0.5f, -1.0f, 0.5f);
@@ -269,8 +273,95 @@ public final strictfp class ResampledImageTest extends TestCase {
assertResultEquals(pt, 1.5f, 1.5f, 10.0f); // Lower right corner
}
+ /**
+ * Verifies that a pixel value in the image created by {@link #createScaledByTwo(Interpolation,
int, int)}
+ * is equals to the expected value.
+ *
+ * @param pt a pixel iterator over the resampled image. Its position will be modified.
+ * @param x <var>x</var> coordinate in the source image.
+ * @param y <var>y</var> coordinate in the source image.
+ * @param expected the expected value.
+ */
private static void assertResultEquals(final PixelIterator pt, float x, float y, float
expected) {
+ // Multiplication by 2 is for converting source coordinates to target coordinates.
pt.moveTo((int) (x*2), (int) (y*2));
assertEquals(expected, pt.getSampleFloat(0), 1E-9f);
}
+
+ /**
+ * Resamples a single-tiled and single-banded image with values 1 everywhere except in
center.
+ * The {@linkplain #source} is a 3×3 image with the following values:
+ *
+ * <blockquote><pre>
+ * 1 1 1
+ * 1 2 1
+ * 1 1 1
+ * </pre></blockquote>
+ *
+ * The {@linkplain #target} is a 9×9 image computed using the given interpolation method.
+ * It is caller's responsibility to verify the result.
+ *
+ * @param interpolation the interpolation method to test.
+ * @return the resampled raster values.
+ */
+ private float[] resampleSimpleImage(final Interpolation interpolation) throws NoninvertibleTransformException
{
+ source = RasterFactory.createGrayScaleImage(DataBuffer.TYPE_FLOAT, 3, 3, 1, 0, 0,
2);
+ final WritableRaster raster = ((BufferedImage) source).getRaster();
+
+ raster.setSample(0, 0, 0, 1); raster.setSample(1, 0, 0, 1); raster.setSample(2,
0, 0, 1);
+ raster.setSample(0, 1, 0, 1); raster.setSample(1, 1, 0, 2); raster.setSample(2,
1, 0, 1);
+ raster.setSample(0, 2, 0, 1); raster.setSample(1, 2, 0, 1); raster.setSample(2,
2, 0, 1);
+
+ sourceToTarget = AffineTransform.getTranslateInstance(-0.5, -0.5);
+ sourceToTarget.scale(3, 3);
+ sourceToTarget.translate(0.5, 0.5);
+ target = new ResampledImage(new Rectangle(9, 9),
+ new AffineTransform2D(sourceToTarget.createInverse()), source, interpolation,
null);
+
+ assertEquals("numXTiles", 1, target.getNumXTiles());
+ assertEquals("numYTiles", 1, target.getNumYTiles());
+ final DataBufferFloat data = (DataBufferFloat) target.getTile(0,0).getDataBuffer();
+ assertEquals("numBanks", 1, data.getNumBanks());
+ return data.getData();
+ }
+
+ /**
+ * Checks all values of a nearest-neighbor interpolation on a simple image.
+ *
+ * @throws NoninvertibleTransformException if the test did not setup the transform correctly.
+ */
+ @Test
+ public void verifyNearestResults() throws NoninvertibleTransformException {
+ assertArrayEquals(new float[] {
+ 1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,
+ 1,1,1,2,2,2,1,1,1,
+ 1,1,1,2,2,2,1,1,1,
+ 1,1,1,2,2,2,1,1,1,
+ 1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1
+ }, resampleSimpleImage(Interpolation.NEAREST), (float) STRICT);
+ }
+
+ /**
+ * Checks all values of a bilinear interpolation on a simple image.
+ *
+ * @throws NoninvertibleTransformException if the test did not setup the transform correctly.
+ */
+ @Test
+ public void verifyBilinearResults() throws NoninvertibleTransformException {
+ assertArrayEquals(new float[] {
+ 1.111111111f, 1f, 0.888888888f, 0.777777777f, 0.666666666f, 0.777777777f, 0.888888888f,
1f, 1.111111111f,
+ 1.000000000f, 1f, 1.000000000f, 1.000000000f, 1.000000000f, 1.000000000f, 1.000000000f,
1f, 1.000000000f,
+ 0.888888888f, 1f, 1.111111111f, 1.222222222f, 1.333333333f, 1.222222222f, 1.111111111f,
1f, 0.888888888f,
+ 0.777777777f, 1f, 1.222222222f, 1.444444444f, 1.666666666f, 1.444444444f, 1.222222222f,
1f, 0.777777777f,
+ 0.666666666f, 1f, 1.333333333f, 1.666666666f, 2f, 1.666666666f, 1.333333333f,
1f, 0.666666666f,
+ 0.777777777f, 1f, 1.222222222f, 1.444444444f, 1.666666666f, 1.444444444f, 1.222222222f,
1f, 0.777777777f,
+ 0.888888888f, 1f, 1.111111111f, 1.222222222f, 1.333333333f, 1.222222222f, 1.111111111f,
1f, 0.888888888f,
+ 1.000000000f, 1f, 1.000000000f, 1.000000000f, 1.000000000f, 1.000000000f, 1.000000000f,
1f, 1.000000000f,
+ 1.111111111f, 1f, 0.888888888f, 0.777777777f, 0.666666666f, 0.777777777f, 0.888888888f,
1f, 1.111111111f
+ }, resampleSimpleImage(Interpolation.BILINEAR), (float) STRICT);
+ }
}
|