You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2021/11/08 16:31:01 UTC
[sis] 03/03: Merge branch 'fix/mask-image' into geoapi-4.0
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 67a64fdf85a73ec1dd182b1bf301f9e024d97700
Merge: f87196f 7b1ee54
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Mon Nov 8 17:12:45 2021 +0100
Merge branch 'fix/mask-image' into geoapi-4.0
.../java/org/apache/sis/image/MaskedImageTest.java | 255 +++++++++++++++++++++
.../apache/sis/test/suite/FeatureTestSuite.java | 3 +-
2 files changed, 257 insertions(+), 1 deletion(-)
diff --cc core/sis-feature/src/test/java/org/apache/sis/image/MaskedImageTest.java
index 0000000,dc9e8ec..441574c
mode 000000,100644..100644
--- a/core/sis-feature/src/test/java/org/apache/sis/image/MaskedImageTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/image/MaskedImageTest.java
@@@ -1,0 -1,237 +1,255 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License. You may obtain a copy of the License at
++ *
++ * http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
+ package org.apache.sis.image;
+
+ import java.awt.Color;
+ import java.awt.Graphics;
++import java.awt.Point;
+ import java.awt.Polygon;
+ import java.awt.Rectangle;
+ import java.awt.image.BufferedImage;
+ import java.awt.image.DataBuffer;
+ import java.awt.image.IndexColorModel;
+ import java.awt.image.Raster;
+ import java.awt.image.RenderedImage;
+ import java.awt.image.WritableRaster;
+ import org.apache.sis.internal.coverage.j2d.WritableTiledImage;
+ import org.apache.sis.test.FeatureAssert;
+ import org.apache.sis.util.ArgumentChecks;
++import org.apache.sis.test.TestCase;
+ import org.junit.Test;
+
+ /**
+ * TODO: test with big images
+ * TODO: when upgrading to Junit 5 use parameterized tests to avoid duplication of test methods
+ */
-public class MaskedImageTest {
++public final strictfp class MaskedImageTest extends TestCase {
+
+ private static final Polygon CONVEX_POLYGON = new Polygon(
+ new int[] {3, 8, 8, 3, 3},
+ new int[] {3, 3, 6, 6, 3},
+ 5);
+
+ private static final Polygon CONCAVE_POLYGON = new Polygon(
+ new int[] { 1, 1, 7, 7, 5, 5, 2, 2 },
+ new int[] { 1, 6, 6, 3, 3, 4, 4, 1 },
+ 8);
+
+ private static final int[] BASE_CANVAS = {
+ 0, 0, 0, 0, 1, 1, 1, 1,
+ 0, 0, 0, 0, 1, 1, 1, 1,
+ 0, 0, 0, 0, 1, 1, 1, 1,
+ 0, 0, 0, 0, 1, 1, 1, 1,
+ 3, 3, 3, 3, 2, 2, 2, 2,
+ 3, 3, 3, 3, 2, 2, 2, 2,
+ 3, 3, 3, 3, 2, 2, 2, 2,
+ 3, 3, 3, 3, 2, 2, 2, 2
+ };
+
+ @Test
+ public void noErrorOnEmptyMasks() {
+ final BufferedImage source = monoTile();
+ final RenderedImage masked = new MaskedImage(source, new Polygon(), true, new Number[]{ 127 });
+ FeatureAssert.assertPixelsEqual(source, null, masked, null);
+ }
+
+ @Test
+ public void fill_MONO_tile_INside_conVEX_polygon() {
+ maskInsideConvexPolygon(monoTile());
+ }
+
+ @Test
+ public void fill_MONO_tile_OUTside_conVEX_polygon() {
+ maskOutsideConvexPolygon(monoTile());
+ }
+
+ @Test
+ public void fill_MULTI_tile_INside_conVEX_polygon() {
+ maskInsideConvexPolygon(multiTile());
+ }
+
+ @Test
+ public void fill_MULTI_tile_OUTside_conVEX_polygon() {
+ maskOutsideConvexPolygon(multiTile());
+ }
+
+ @Test
+ public void fill_MONO_tile_INside_conCAVE_polygon() {
+ fillInsideConcavePolygon(monoTile());
+ }
+
+ @Test
+ public void fill_MONO_tile_OUTside_conCAVE_polygon() {
+ fillOutsideConcavePolygon(monoTile());
+ }
+
+ @Test
+ public void fill_MULTI_tile_INside_conCAVE_polygon() {
+ fillInsideConcavePolygon(multiTile());
+ }
+
+ @Test
+ public void fill_MULTI_tile_OUTside_conCAVE_polygon() {
+ fillOutsideConcavePolygon(multiTile());
+ }
+
+ private void maskInsideConvexPolygon(RenderedImage source) {
+ final RenderedImage masked = new MaskedImage(source, CONVEX_POLYGON, true, new Number[]{ 4 });
+ final RenderedImage expected = monoTile(new int[] {
+ 0, 0, 0, 0, 1, 1, 1, 1,
+ 0, 0, 0, 0, 1, 1, 1, 1,
+ 0, 0, 0, 0, 1, 1, 1, 1,
+ 0, 0, 0, 4, 4, 4, 4, 4,
+ 3, 3, 3, 4, 4, 4, 4, 4,
+ 3, 3, 3, 4, 4, 4, 4, 4,
+ 3, 3, 3, 3, 2, 2, 2, 2,
+ 3, 3, 3, 3, 2, 2, 2, 2
+ });
+ FeatureAssert.assertPixelsEqual(expected, null, masked, null);
+ }
+
+ private void maskOutsideConvexPolygon(final RenderedImage source) {
+ final RenderedImage masked = new MaskedImage(source, CONVEX_POLYGON, false, new Number[]{ 4 });
+ final RenderedImage expected = monoTile(new int[] {
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 0, 1, 1, 1, 1,
+ 4, 4, 4, 3, 2, 2, 2, 2,
+ 4, 4, 4, 3, 2, 2, 2, 2,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4
+ });
+ FeatureAssert.assertPixelsEqual(expected, null, masked, null);
+ }
+
+ private void fillInsideConcavePolygon(final RenderedImage source) {
+ final RenderedImage masked = new MaskedImage(source, CONCAVE_POLYGON, true, new Number[]{ 4 });
+ final RenderedImage expected = monoTile(new int[] {
+ 0, 0, 0, 0, 1, 1, 1, 1,
+ 0, 4, 0, 0, 1, 1, 1, 1,
+ 0, 4, 0, 0, 1, 1, 1, 1,
+ 0, 4, 0, 0, 1, 4, 4, 1,
+ 3, 4, 4, 4, 4, 4, 4, 2,
+ 3, 4, 4, 4, 4, 4, 4, 2,
+ 3, 3, 3, 3, 2, 2, 2, 2,
+ 3, 3, 3, 3, 2, 2, 2, 2
+ });
+ FeatureAssert.assertPixelsEqual(expected, null, masked, null);
+ }
+
+ private void fillOutsideConcavePolygon(final RenderedImage source) {
+ final RenderedImage masked = new MaskedImage(source, CONCAVE_POLYGON, false, new Number[]{ 4 });
+ final RenderedImage expected = monoTile(new int[] {
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 0, 4, 4, 4, 4, 4, 4,
+ 4, 0, 4, 4, 4, 4, 4, 4,
+ 4, 0, 4, 4, 4, 1, 1, 4,
+ 4, 3, 3, 3, 2, 2, 2, 4,
+ 4, 3, 3, 3, 2, 2, 2, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4
+ });
+ FeatureAssert.assertPixelsEqual(expected, null, masked, null);
+ }
+
+ private RenderedImage multiTile() {
+ return new WritableTiledImage(null, colorPalette(), 8, 8, 0, 0,
- tile(0), tile(1), tile(3), tile(2));
++ tile(0, 0), tile(1, 1), tile(2, 3), tile(3, 2));
+ }
+
- private WritableRaster tile(int fillValue) {
- final WritableRaster tile = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 4, 4, 1, null);
++ private static WritableRaster tile(final int n, final int fillValue) {
++ final WritableRaster tile = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 4, 4, 1, new Point((n % 2)*4, (n / 2)*4));
+ final WritablePixelIterator it = new PixelIterator.Builder().createWritable(tile);
+ while (it.next()) it.setSample(0, fillValue);
+ return tile;
+ }
+
+ /**
+ * Base colors are shades of blue. The last (fifth) color used for masks is green.
+ * @return
+ */
+ private static IndexColorModel colorPalette() {
+ byte[] reds = { 0, 0, 0, 0, 0 };
+ byte[] greens = { 0, 0, 0, 0, 127 };
+ byte[] blues = { 0, 50, 95, 127, 0 };
+ return new IndexColorModel(Byte.SIZE, 5, reds, greens, blues);
+ }
+
+ /**
+ *
+ * @return Base test image, based on {@link #colorPalette() indexed color model}.
+ */
+ private static BufferedImage monoTile() {
+ return monoTile(BASE_CANVAS);
+ }
+
+ private static BufferedImage monoTile(int[] pixels) {
+ ArgumentChecks.ensureDimensionMatches("Input raster must be 8x8", 64, pixels);
+ final BufferedImage image = new BufferedImage(8, 8, BufferedImage.TYPE_BYTE_INDEXED, colorPalette());
+ final WritableRaster raster = image.getRaster();
+ raster.setPixels(0, 0, 8, 8, pixels);
+ return image;
+ }
+
+ /**
+ * Draw given polygon into source image, to display expected result in debugger.
+ */
+ private static BufferedImage debugGeometryImage(final Polygon geometry) {
+ final BufferedImage source = monoTile();
+ final BufferedImage debugImg = new BufferedImage(source.getWidth(), source.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
+ final Graphics painter = debugImg.getGraphics();
+ painter.drawImage(source, 0, 0, null);
+
+ final Rectangle bounds = geometry.getBounds();
+ final Rectangle enlargedBounds = new Rectangle(bounds.x - 1, bounds.y - 1, bounds.width + 2, bounds.height + 2);
+ final Rectangle intersection = enlargedBounds.intersection(debugImg.getRaster().getBounds());
+ painter.setColor(Color.RED);
+ painter.fillRect(intersection.x, intersection.y, intersection.width, intersection.height);
+ painter.setColor(Color.GREEN);
+ painter.fillPolygon(geometry);
+ painter.dispose();
+
+ return debugImg;
+ }
+
+
+ /**
+ * Useful in IntelliJ: allow to display input image in debugger: Add a new watch calling this method on wanted image.
+ * <em>WARNINGS:</em> works only for current test case:
+ * <ul>
+ * <li> it assume input image is compatible with test {@link #colorPalette() color palette}.</li>
+ * <li>Work only on single tile images.</li>
+ * </ul>
+ *
+ * @param source The image to display.
+ * @return The image directly displayable through debugger.
+ */
+ private static BufferedImage debug(final RenderedImage source) {
+ Raster tile = source.getTile(source.getMinTileX(), source.getMinTileY());
+ final int width, height;
+
+ tile = tile.createTranslatedChild(0, 0);
+ width = tile.getWidth();
+ height = tile.getHeight();
+
+ final BufferedImage view = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_INDEXED, colorPalette());
+ view.getRaster().setRect(tile);
+
+ return view;
+ }
+ }
diff --cc core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
index 32834e5,32834e5..9f2cf45
--- a/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
@@@ -26,7 -26,7 +26,7 @@@ import org.junit.runners.Suite
*
* @author Martin Desruisseaux (Geomatys)
* @author Johann Sorel (Geomatys)
-- * @version 1.1
++ * @version 1.2
* @since 0.5
* @module
*/
@@@ -97,6 -97,6 +97,7 @@@
org.apache.sis.image.InterpolationTest.class,
org.apache.sis.image.ResamplingGridTest.class,
org.apache.sis.image.ResampledImageTest.class,
++ org.apache.sis.image.MaskedImageTest.class,
org.apache.sis.image.BandedSampleConverterTest.class,
org.apache.sis.image.ImageCombinerTest.class,
org.apache.sis.image.ImageProcessorTest.class,