sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: Add ImageProcessor.selectBands(…) method.
Date Sun, 19 Jul 2020 20:56:08 GMT
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


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new ac91b8a  Add ImageProcessor.selectBands(…) method.
ac91b8a is described below

commit ac91b8a774bfed23b08f34c116d5edf1ef660937
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Sun Jul 19 09:36:17 2020 +0200

    Add ImageProcessor.selectBands(…) method.
---
 .../java/org/apache/sis/coverage/CategoryList.java |   2 +-
 .../org/apache/sis/coverage/grid/GridCoverage.java |   6 +-
 .../java/org/apache/sis/image/BandSelectImage.java | 108 +++++++++++++++
 .../java/org/apache/sis/image/ImageProcessor.java  |  19 +++
 .../org/apache/sis/image/SourceAlignedImage.java   |  14 ++
 .../internal/coverage/j2d/ColorModelFactory.java   |  20 ++-
 .../coverage/j2d/MultiBandsIndexColorModel.java    |  42 +++++-
 .../internal/coverage/j2d/ScaledColorModel.java    |   7 +
 .../internal/coverage/j2d/ScaledColorSpace.java    |  18 ++-
 .../org/apache/sis/image/BandSelectImageTest.java  | 150 +++++++++++++++++++++
 .../apache/sis/image/StatisticsCalculatorTest.java |   2 +-
 .../apache/sis/test/suite/FeatureTestSuite.java    |   1 +
 12 files changed, 379 insertions(+), 10 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/CategoryList.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/CategoryList.java
index fb14bcc..4f3f8de 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/CategoryList.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/CategoryList.java
@@ -151,7 +151,7 @@ final class CategoryList extends AbstractList<Category> implements
MathTransform
      *
      * <ul>
      *   <li>+0 means to leave the NaN value as-is. In such case, casting the NaN value
to an integer will
-     *     produce 0 (so the 0 value is not set explicitely, but obtained as a result of
casting to integer).
+     *     produce 0 (so the 0 value is not set explicitly, but obtained as a result of casting
to integer).
      *     This action can be taken only if no category include the 0 value, or if 0 is for
the background.</li>
      *   <li>Any non-zero and non-NaN value means to use that value directly. In such
case, the value should
      *     be {@link SampleDimension#background}.</li>
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
index a625761..d4a72ba 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
@@ -244,9 +244,9 @@ public abstract class GridCoverage {
      * @return the image which compute converted values from the given source.
      */
     final RenderedImage convert(final RenderedImage source, final int dataType, final MathTransform1D[]
converters) {
-        final int visibleBands = Math.max(0, ImageUtilities.getVisibleBand(source));
-        final ColorModel cm = ColorModelFactory.createColorModel(dataType, sampleDimensions.length,
visibleBands,
-                                    sampleDimensions[visibleBands].getCategories(), ColorModelFactory.GRAYSCALE);
+        final int visibleBand = Math.max(0, ImageUtilities.getVisibleBand(source));
+        final ColorModel cm = ColorModelFactory.createColorModel(dataType, sampleDimensions.length,
visibleBand,
+                                    sampleDimensions[visibleBand].getCategories(), ColorModelFactory.GRAYSCALE);
 
        return BandedSampleConverter.create(source, null, dataType, cm, getRanges(), converters);
     }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/BandSelectImage.java b/core/sis-feature/src/main/java/org/apache/sis/image/BandSelectImage.java
new file mode 100644
index 0000000..b27fcc4
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/BandSelectImage.java
@@ -0,0 +1,108 @@
+/*
+ * 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.image.Raster;
+import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+import java.awt.image.WritableRaster;
+import java.awt.image.ColorModel;
+import org.apache.sis.util.ArraysExt;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.internal.coverage.j2d.ImageUtilities;
+import org.apache.sis.internal.coverage.j2d.ColorModelFactory;
+
+
+/**
+ * Selects or reorder bands from a source image. This operation avoid copying sample values;
+ * it works by modifying the sample model and color model.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+final class BandSelectImage extends SourceAlignedImage {
+    /**
+     * The selected bands.
+     */
+    private final int[] bands;
+
+    /**
+     * Creates a new "band select" operation for the given source.
+     * It is caller responsibility to verify the validity of given {@code bands} indices.
+     *
+     * @param  source  the image in which to select bands.
+     * @param  cm      the color model to associate to this image.
+     * @param  bands   the bands to select. Should be a clone of user-specified argument
+     *                 (this constructor retains the given array reference as-is, without
cloning).
+     */
+    private BandSelectImage(final RenderedImage source, final ColorModel cm, final int[]
bands) {
+        super(source, cm, source.getSampleModel().createSubsetSampleModel(bands));
+        this.bands = bands;
+    }
+
+    /**
+     * Creates a new "band select" operation for the given source.
+     *
+     * @param  source  the image in which to select bands.
+     * @param  bands   the bands to select.
+     */
+    static RenderedImage create(final RenderedImage source, final int[] bands) {
+        final int numBands = ImageUtilities.getNumBands(source);
+        if (bands.length == numBands && ArraysExt.isRange(0, bands)) {
+            return source;
+        }
+        ArgumentChecks.ensureNonEmpty("bands", bands, 0, numBands - 1, false);
+        final ColorModel cm = ColorModelFactory.createSubsetColorModel(source.getColorModel(),
bands);
+        /*
+         * If the image is an instance of `BufferedImage`, create the subset immediately
+         * (reminder: this operation will not copy pixel data). It allows us to return a
+         * new instance of `BufferedImage`, which has optimization in Java2D.
+         */
+        if (source instanceof BufferedImage) {
+            final BufferedImage bi = (BufferedImage) source;
+            return new BufferedImage(cm,
+                    bi.getRaster().createWritableChild(0, 0, bi.getWidth(), bi.getHeight(),
0, 0, bands),
+                    bi.isAlphaPremultiplied(), null);
+        }
+        return new BandSelectImage(source, cm, bands.clone());
+    }
+
+    /**
+     * Creates a raster sharing the same data buffer than the source image but showing only
a subset of the bands.
+     *
+     * @param  tileX     the column index of the tile to compute.
+     * @param  tileY     the row index of the tile to compute.
+     * @param  previous  ignored.
+     */
+    @Override
+    protected Raster computeTile(final int tileX, final int tileY, final WritableRaster previous)
{
+        final Raster parent = getSource().getTile(tileX, tileY);
+        final int x = parent.getMinX();
+        final int y = parent.getMinY();
+        /*
+         * The following method call is a bit inefficient because it will create a new `SampleModel`
for each tile
+         * and all those sample models are identical to the one we created at `BandSelectImage`
construction time.
+         * But it does not seem possible to tell `Raster` to share the existing `SampleModel`
instance.
+         *
+         * Alternatively we could have tried to do the work of `Raster.createChild(…)`
method ourselves.
+         * But we don't because that method is overridden in various Java2D `SunWritableRaster`
classes.
+         */
+        return parent.createChild(x, y, parent.getWidth(), parent.getHeight(), x, y, bands);
+    }
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java b/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java
index e87ed5f..661da66 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java
@@ -591,6 +591,25 @@ public class ImageProcessor implements Cloneable {
     }
 
     /**
+     * Selects a subset of bands in the given image. This method can also be used for changing
band order
+     * or repeating the same band from the source image. If the specified {@code bands} are
the same than
+     * the source image bands in the same order, then {@code source} is returned directly.
+     *
+     * <p>This method returns an image sharing the same data buffer than the source
image;
+     * pixel values are not copied. Consequently changes in the source image are reflected
+     * immediately in the returned image.</p>
+     *
+     * @param  source  the image in which to select bands.
+     * @param  bands   indices of bands to retain.
+     * @return image width selected bands.
+     * @throws IllegalArgumentException if a band index is invalid.
+     */
+    public RenderedImage selectBands(final RenderedImage source, final int... bands) {
+        ArgumentChecks.ensureNonNull("source", source);
+        return BandSelectImage.create(source, bands);
+    }
+
+    /**
      * Creates a new image which will resample the given image. The resampling operation
is defined
      * by a non-linear transform from the <em>new</em> image to the specified
<em>source</em> image.
      * That transform should map {@linkplain org.opengis.referencing.datum.PixelInCell#CELL_CENTER
pixel centers}.
diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/SourceAlignedImage.java b/core/sis-feature/src/main/java/org/apache/sis/image/SourceAlignedImage.java
index f8085a5..bc654db 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/SourceAlignedImage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/SourceAlignedImage.java
@@ -39,6 +39,18 @@ abstract class SourceAlignedImage extends ComputedImage {
     private final ColorModel colorModel;
 
     /**
+     * Creates a new image with the given source, color model and sample model.
+     *
+     * @param  source       source of this image. Shall not be null.
+     * @param  colorModel   the color model of the new image.
+     * @param  sampleModel  the sample model of the new image.
+     */
+    SourceAlignedImage(final RenderedImage source, final ColorModel colorModel, final SampleModel
sampleModel) {
+        super(sampleModel, source);
+        this.colorModel = colorModel;
+    }
+
+    /**
      * Creates a new image with the given source and a sample model derived from the color
model.
      * The new image will have the same tile size than the given image.
      *
@@ -110,6 +122,8 @@ abstract class SourceAlignedImage extends ComputedImage {
     @Override public final int getMinTileY()        {return getSource().getMinTileY();}
     @Override public final int getNumXTiles()       {return getSource().getNumXTiles();}
     @Override public final int getNumYTiles()       {return getSource().getNumYTiles();}
+    @Override public final int getTileWidth()       {return getSource().getTileWidth();}
+    @Override public final int getTileHeight()      {return getSource().getTileHeight();}
     @Override public final int getTileGridXOffset() {return getSource().getTileGridXOffset();}
     @Override public final int getTileGridYOffset() {return getSource().getTileGridYOffset();}
 }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorModelFactory.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorModelFactory.java
index 5117913..d083d63 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorModelFactory.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorModelFactory.java
@@ -470,6 +470,24 @@ public final class ColorModelFactory {
     }
 
     /**
+     * Creates a color model with only a subset of the bands of the given color model.
+     *
+     * @param  cm     the color model, or {@code null}.
+     * @param  bands  the bands to select.
+     * @return the subset color model, or {@code null} if it can not be created.
+     */
+    public static ColorModel createSubsetColorModel(final ColorModel cm, final int[] bands)
{
+        if (cm instanceof MultiBandsIndexColorModel) {
+            return unique(((MultiBandsIndexColorModel) cm).createSubsetColorModel(bands));
+        }
+        if (cm instanceof ScaledColorModel) {
+            return unique(((ScaledColorModel) cm).createSubsetColorModel(bands));
+        }
+        // TODO: handle other color models.
+        return null;
+    }
+
+    /**
      * Appends a description of the given color space in the given buffer.
      * This is used for {@code toString()} method implementations.
      *
@@ -495,7 +513,7 @@ public final class ColorModelFactory {
      * @param  cm   the color model for which to get a unique instance.
      * @return a unique (shared) instance of the given color model.
      */
-    public static <T extends ColorModel> T unique(T cm) {
+    private static <T extends ColorModel> T unique(T cm) {
         ColorModelPatch<T> c = new ColorModelPatch<>(cm);
         c = CACHE.unique(c);
         return c.cm;
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/MultiBandsIndexColorModel.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/MultiBandsIndexColorModel.java
index 5aca711..eec2592 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/MultiBandsIndexColorModel.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/MultiBandsIndexColorModel.java
@@ -30,17 +30,17 @@ import java.awt.image.SinglePixelPackedSampleModel;
 /**
  * An {@link IndexColorModel} tolerant with image having more than one band.
  * This class can support only the types supported by {@code IndexColorModel}
- * parent class. As of Java 10 they are restricted to {@link DataBuffer#TYPE_BYTE}
+ * parent class. As of Java 14 they are restricted to {@link DataBuffer#TYPE_BYTE}
  * and {@code DataBuffer#TYPE_USHORT}.
  *
- * <p><b>Reminder:</b> {@link #getNumComponents()} will returns 3 or 4
no matter
+ * <p><b>Reminder:</b> {@link #getNumComponents()} will return 3 or 4 no
matter
  * how many bands were specified to the constructor. This is not specific to this class;
  * {@code IndexColorModel} behave that way. So we can't rely on this method for checking
  * the number of bands.</p>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @author  Andrea Aime (TOPP)
- * @version 1.0
+ * @version 1.1
  * @since   1.0
  * @module
  */
@@ -89,6 +89,42 @@ final class MultiBandsIndexColorModel extends IndexColorModel {
     }
 
     /**
+     * Creates a new color model with only a subset of the bands in this color model.
+     *
+     * <p><b>Note:</b> the new color model will use a copy of the color
map of this color model.
+     * There is no way to share the {@code int[]} array of ARGB values between two {@link
IndexColorModel}s.</p>
+     */
+    final IndexColorModel createSubsetColorModel(final int[] bands) {
+        final int     bits        = getPixelSize();
+        final int[]   cmap        = getARGB();
+        final boolean hasAlpha    = hasAlpha();
+        final int     transparent = getTransparentPixel();
+        if (bands.length == 1) {
+            return new IndexColorModel(bits, cmap.length, cmap, 0, hasAlpha, transparent,
transferType);
+        }
+        int vb = 0;
+        for (int i=0; i<bands.length; i++) {
+            if (bands[i] == visibleBand) {
+                vb = i;
+                break;
+            }
+        }
+        return new MultiBandsIndexColorModel(bits, cmap.length, cmap, 0, hasAlpha, transparent,
transferType, bands.length, vb);
+    }
+
+    /**
+     * Returns all ARGB colors in this color model.
+     * This is a copy of the {@code cmap} used by this color model.
+     */
+    private int[] getARGB() {
+        final int[] cmap = new int[getMapSize()];
+        for (int i=0; i<cmap.length; i++) {
+            cmap[i] = getRGB(i);
+        }
+        return cmap;
+    }
+
+    /**
      * Converts a RGB color to a representation of a pixel in this color model.
      * This method returns an array with a length equal to the number of bands specified
to
      * the constructor ({@code IndexColorModel} would returns an array of length 1). All
array
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorModel.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorModel.java
index 3136ab3..4ec32ab 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorModel.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorModel.java
@@ -67,6 +67,13 @@ final class ScaledColorModel extends ComponentColorModel {
     }
 
     /**
+     * Creates a new color model with only a subset of the bands in this color model.
+     */
+    final ScaledColorModel createSubsetColorModel(final int[] bands) {
+        return new ScaledColorModel(new ScaledColorSpace(cs, bands), transferType);
+    }
+
+    /**
      * Defined for consistency but should not be used.
      */
     @Override public int getRed  (int    value) {return (getRGB(value) >>> 2*Byte.SIZE)
& MASK;}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorSpace.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorSpace.java
index 13ccfa3..95c65cd 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorSpace.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorSpace.java
@@ -61,7 +61,7 @@ final class ScaledColorSpace extends ColorSpace {
     final int visibleBand;
 
     /**
-     * Creates a color model for the given range of values.
+     * Creates a color space for the given range of values.
      * The given range does not need to be exact; values outside that range will be clamped.
      *
      * @param  numComponents  the number of components.
@@ -77,6 +77,22 @@ final class ScaledColorSpace extends ColorSpace {
     }
 
     /**
+     * Creates a color space for the same range of values than the given space, but a subset
of the bands.
+     */
+    ScaledColorSpace(final ScaledColorSpace parent, final int[] bands) {
+        super(TYPE_GRAY, bands.length);
+        scale  = parent.scale;
+        offset = parent.offset;
+        for (int i=0; i<bands.length; i++) {
+            if (bands[i] == parent.visibleBand) {
+                visibleBand = i;
+                return;
+            }
+        }
+        visibleBand = 0;
+    }
+
+    /**
      * Returns a RGB color for a sample value.
      *
      * @param  samples  sample values in the raster.
diff --git a/core/sis-feature/src/test/java/org/apache/sis/image/BandSelectImageTest.java
b/core/sis-feature/src/test/java/org/apache/sis/image/BandSelectImageTest.java
new file mode 100644
index 0000000..bbcdac3
--- /dev/null
+++ b/core/sis-feature/src/test/java/org/apache/sis/image/BandSelectImageTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.util.Random;
+import java.awt.image.DataBuffer;
+import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
+import java.awt.image.RenderedImage;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.IndexColorModel;
+import org.apache.sis.internal.coverage.j2d.ColorModelFactory;
+import org.apache.sis.internal.coverage.j2d.ImageUtilities;
+import org.apache.sis.test.TestCase;
+import org.junit.Test;
+
+import static org.apache.sis.test.FeatureAssert.*;
+
+
+/**
+ * Tests {@link BandSelectImage}.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+public final strictfp class BandSelectImageTest extends TestCase {
+    /**
+     * Arbitrary size for the test image.
+     */
+    private static final int WIDTH = 3, HEIGHT = 4;
+
+    /**
+     * The source image as an instance of custom implementation.
+     */
+    private TiledImageMock image;
+
+    /**
+     * The source image as a {@link BufferedImage} instance.
+     */
+    private BufferedImage bufferedImage;
+
+    /**
+     * Creates a dummy image for testing purpose. This image will contain the given amount
of bands.
+     * One band contains deterministic values and all other bands contain random values.
+     * The image is assigned to {@link #bufferedImage} and {@link #image} fields.
+     *
+     * @param  numBands     number of bands in the image to create.
+     * @param  checkedBand  band in which to put deterministic values.
+     * @param  icm          {@code true} for using index color model, or {@code false} for
scaled color model.
+     */
+    private void createImage(final int numBands, final int checkedBand, final boolean icm)
{
+        image = new TiledImageMock(DataBuffer.TYPE_BYTE, numBands, 0, 0, WIDTH, HEIGHT, WIDTH,
HEIGHT, 0, 0);
+        image.initializeAllTiles(checkedBand);
+        final Random random = new Random();
+        for (int i=0; i<numBands; i++) {
+            if (i != checkedBand) {
+                image.setRandomValues(i, random, 100);
+            }
+        }
+        image.validate();
+        final ColorModel cm;
+        if (icm) {
+            final int[] ARGB = new int[256];
+            ColorModelFactory.expand(new int[] {0xFF000000, 0xFFFFFFFF}, ARGB, 0, ARGB.length);
+            cm = ColorModelFactory.createIndexColorModel(numBands, checkedBand, ARGB, -1);
+        } else {
+            cm = ColorModelFactory.createGrayScale(DataBuffer.TYPE_BYTE, numBands, checkedBand,
Byte.MIN_VALUE, Byte.MAX_VALUE);
+        }
+        bufferedImage = new BufferedImage(cm, (WritableRaster) image.getTile(0, 0), false,
null);
+    }
+
+    /**
+     * Verifies that the given image contains the data expected for the {@code checkedBand}.
+     */
+    private static void verify(final RenderedImage image, final int numBands, final int checkedBand)
{
+        final Raster tile = image.getTile(0,0);
+        assertEquals("numBands", numBands, tile.getNumBands());
+        assertEquals("numBands", numBands, ImageUtilities.getNumBands(image));
+        assertEquals("sampleModel", image.getSampleModel(), tile.getSampleModel());
+        assertValuesEqual(tile, checkedBand, new int[][] {
+            {100, 101, 102},
+            {110, 111, 112},
+            {120, 121, 122},
+            {130, 131, 132}
+        });
+    }
+
+    /**
+     * Tests bands selection in an image using index color model.
+     */
+    @Test
+    public void testIndexColorModel() {
+        createImage(3, 1, true);
+        final ImageProcessor processor = new ImageProcessor();
+        assertSame(image,          processor.selectBands(image,         0, 1, 2));
+        assertSame(bufferedImage,  processor.selectBands(bufferedImage, 0, 1, 2));
+
+        RenderedImage test = processor.selectBands(image, 1);
+        verify(test, 1, 0);
+
+        test = processor.selectBands(image, 0, 1);
+        verify(test, 2, 1);
+
+        test = processor.selectBands(bufferedImage, 1);
+        assertInstanceOf("image", BufferedImage.class, test);
+        assertEquals("colorModel", IndexColorModel.class, test.getColorModel().getClass());
+        verify(test, 1, 0);
+
+        test = processor.selectBands(bufferedImage, 0, 1);
+        assertInstanceOf("image", BufferedImage.class, test);
+        assertInstanceOf("colorModel", IndexColorModel.class, test.getColorModel());
+        verify(test, 2, 1);
+    }
+
+    /**
+     * Tests bands selection in an image using scaled color model.
+     */
+    @Test
+    public void testScaledColorModel() {
+        createImage(4, 2, true);
+        final ImageProcessor processor = new ImageProcessor();
+        assertSame(image,         processor.selectBands(image,         0, 1, 2, 3));
+        assertSame(bufferedImage, processor.selectBands(bufferedImage, 0, 1, 2, 3));
+
+        RenderedImage test = processor.selectBands(image, 3, 0, 2);
+        verify(test, 3, 2);
+
+        test = processor.selectBands(bufferedImage, 3, 0, 2);
+        assertInstanceOf("image", BufferedImage.class, test);
+        assertNotNull("colorModel", test.getColorModel());
+        verify(test, 3, 2);
+    }
+}
diff --git a/core/sis-feature/src/test/java/org/apache/sis/image/StatisticsCalculatorTest.java
b/core/sis-feature/src/test/java/org/apache/sis/image/StatisticsCalculatorTest.java
index 02d206a..ea9f89b 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/image/StatisticsCalculatorTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/image/StatisticsCalculatorTest.java
@@ -56,7 +56,7 @@ public final strictfp class StatisticsCalculatorTest extends TestCase {
 
     /**
      * Creates a dummy image for testing purpose. This image will contain many small tiles
-     * of two bands. The first bands has deterministic values and the second band contains
+     * of two bands. The first band has deterministic values and the second band contains
      * random values.
      */
     private static TiledImageMock createImage() {
diff --git 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
index bc62e53..1cd0150 100644
--- 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
@@ -83,6 +83,7 @@ import org.junit.runners.Suite;
     org.apache.sis.image.DefaultIteratorTest.class,
     org.apache.sis.image.LinearIteratorTest.class,
     org.apache.sis.image.StatisticsCalculatorTest.class,
+    org.apache.sis.image.BandSelectImageTest.class,
     org.apache.sis.image.InterpolationTest.class,
     org.apache.sis.image.ResamplingGridTest.class,
     org.apache.sis.image.ResampledImageTest.class,


Mime
View raw message