sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 02/02: Add an `ImageUtilities` class for methods to be needed by `GridCoverage2D`.
Date Thu, 19 Dec 2019 18:00:17 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

commit 67c5d18936c47de51f842a93bbcb597f8d42918b
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Thu Dec 19 18:51:17 2019 +0100

    Add an `ImageUtilities` class for methods to be needed by `GridCoverage2D`.
---
 .../sis/internal/coverage/ImageUtilities.java      | 154 +++++++++++++++++++++
 .../sis/internal/coverage/ImageUtilitiesTest.java  |  77 +++++++++++
 .../apache/sis/test/suite/FeatureTestSuite.java    |   1 +
 .../org/apache/sis/util/resources/Vocabulary.java  |  45 ++++++
 .../sis/util/resources/Vocabulary.properties       |   9 ++
 .../sis/util/resources/Vocabulary_fr.properties    |   9 ++
 6 files changed, 295 insertions(+)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/ImageUtilities.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/ImageUtilities.java
new file mode 100644
index 0000000..73fa6aa
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/ImageUtilities.java
@@ -0,0 +1,154 @@
+/*
+ * 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.internal.coverage;
+
+import java.util.Arrays;
+import java.awt.color.ColorSpace;
+import java.awt.image.ColorModel;
+import java.awt.image.PackedColorModel;
+import java.awt.image.RenderedImage;
+import java.awt.image.SampleModel;
+import java.awt.image.SinglePixelPackedSampleModel;
+import org.apache.sis.internal.system.Modules;
+import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.resources.Vocabulary;
+
+
+/**
+ * Utility methods related to images and their color model or sample model.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+public final class ImageUtilities {
+    /**
+     * Do not allow instantiation of this class.
+     */
+    private ImageUtilities() {
+    }
+
+    /**
+     * Returns names of bands based on inspection of the color model.
+     * The bands are identified by {@link Vocabulary.Keys} values for
+     * red, green, blue, cyan, magenta, yellow, black, gray, <i>etc</i>.
+     * If a band can not be identified, then its corresponding value is 0.
+     *
+     * @param  image  the image for which to get band names (can not be null).
+     * @return {@link Vocabulary.Keys} identifying the bands.
+     */
+    @SuppressWarnings("fallthrough")
+    public static short[] bandNames(final RenderedImage image) {
+        final SampleModel sm = image.getSampleModel();
+        final int n;
+        if (sm != null) {
+            n = sm.getNumBands();
+        } else {
+            // Should not happen since SampleModel is essential, but we try to be robust.
+            n = image.getTile(image.getMinTileX(), image.getMinTileY()).getNumBands();
+        }
+        final short[] keys = new short[n];
+        final ColorModel cm = image.getColorModel();
+        if (cm != null) {
+            final ColorSpace cs = cm.getColorSpace();
+            if (cs != null) {
+                /*
+                 * Get one of the following sets of color names (ignoring order for now):
+                 *
+                 *   - Red, Green, Blue
+                 *   - Cyan, Magenta, Yellow, Black
+                 *   - Gray
+                 */
+                switch (cs.getType()) {
+                    case ColorSpace.TYPE_CMYK: {
+                        if (n >= 4)  keys[3] = Vocabulary.Keys.Black;
+                        // Fallthrough
+                    }
+                    case ColorSpace.TYPE_CMY: {
+                        switch (n) {
+                            default: keys[2] = Vocabulary.Keys.Yellow;      // Fallthrough
everywhere.
+                            case 2:  keys[1] = Vocabulary.Keys.Magenta;
+                            case 1:  keys[0] = Vocabulary.Keys.Cyan;
+                            case 0:  break;
+                        }
+                        break;
+                    }
+                    case ColorSpace.TYPE_RGB: {
+                        switch (n) {
+                            default: keys[2] = Vocabulary.Keys.Blue;        // Fallthrough
everywhere.
+                            case 2:  keys[1] = Vocabulary.Keys.Green;
+                            case 1:  keys[0] = Vocabulary.Keys.Red;
+                            case 0:  break;
+                        }
+                        break;
+                    }
+                    case ColorSpace.TYPE_GRAY: {
+                        if (n != 0)  keys[0] = Vocabulary.Keys.Gray;
+                        break;
+                    }
+                }
+                /*
+                 * If the color model has more components than the number of colors,
+                 * then the additional component is an alpha channel.
+                 */
+                final int nc = cm.getNumColorComponents();
+                if (nc < n && nc < cm.getNumComponents()) {
+                    keys[nc] = Vocabulary.Keys.Transparency;
+                }
+                /*
+                 * In current version we do not try to adapt the bands order to the masks.
+                 * A few tests suggest that the following methods provide the same values:
+                 *
+                 *   - PackedColorModel.getMasks()
+                 *   - SinglePixelPackedSampleModel.getBitMasks()
+                 *
+                 * For a BufferedImage.TYPE_INT_ARGB, both methods give in that order:
+                 *
+                 *    masks[0]:  00FF0000     (red)
+                 *    masks[1]:  0000FF00     (green)
+                 *    masks[2]:  000000FF     (blue)
+                 *    masks[3]:  FF000000     (alpha)  —  this last element is absent with
TYPE_INT_RGB.
+                 *
+                 * For a BufferedImage.TYPE_INT_BGR, both methods give in that order:
+                 *
+                 *    masks[0]:  000000FF     (red)
+                 *    masks[1]:  0000FF00     (green)
+                 *    masks[2]:  00FF0000     (blue)
+                 *
+                 * So it looks like that SampleModel already normalizes the color components
+                 * to (Red, Green, Blue) order, at least when the image has been created
with
+                 * a standard constructor. However we do not know yet what would be the behavior
+                 * if masks are not the same. For now we just log a warning.
+                 */
+                int[] m1 = null;
+                int[] m2 = null;
+                if (cm instanceof PackedColorModel) {
+                    m1 = ((PackedColorModel) cm).getMasks();
+                }
+                if (sm instanceof SinglePixelPackedSampleModel) {
+                    m2 = ((SinglePixelPackedSampleModel) sm).getBitMasks();
+                }
+                if (!Arrays.equals(m1, m2)) {
+                    // If this logging happen, we should revisit this method and improve
it.
+                    Logging.getLogger(Modules.RASTER).warning("Band names may be in wrong
order.");
+                }
+            }
+        }
+        return keys;
+    }
+}
diff --git a/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/ImageUtilitiesTest.java
b/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/ImageUtilitiesTest.java
new file mode 100644
index 0000000..ba21f6c
--- /dev/null
+++ b/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/ImageUtilitiesTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.internal.coverage;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+import org.apache.sis.util.resources.Vocabulary;
+import org.apache.sis.test.TestCase;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * Tests {@link ImageUtilities}.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+public final strictfp class ImageUtilitiesTest extends TestCase {
+    /**
+     * Tests {@link ImageUtilities#bandNames(RenderedImage)}.
+     */
+    @Test
+    public void testBandNames() {
+        RenderedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
+        assertArrayEquals(new short[] {
+            Vocabulary.Keys.Red,
+            Vocabulary.Keys.Green,
+            Vocabulary.Keys.Blue,
+            Vocabulary.Keys.Transparency
+        }, ImageUtilities.bandNames(image));
+        /*
+         * Same as above, but without alpha channel.
+         */
+        image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
+        assertArrayEquals(new short[] {
+            Vocabulary.Keys.Red,
+            Vocabulary.Keys.Green,
+            Vocabulary.Keys.Blue
+        }, ImageUtilities.bandNames(image));
+        /*
+         * Same as above but in sample values packed in reverse order. Note that while values
+         * are packed in BGR order, the sample model is still providing the values in RGB
order.
+         * For that reason, the band order below is the same than in above test.
+         */
+        image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_BGR);
+        assertArrayEquals(new short[] {
+            Vocabulary.Keys.Red,
+            Vocabulary.Keys.Green,
+            Vocabulary.Keys.Blue
+        }, ImageUtilities.bandNames(image));
+        /*
+         * One-banded image.
+         */
+        image = new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY);
+        assertArrayEquals(new short[] {
+            Vocabulary.Keys.Gray
+        }, ImageUtilities.bandNames(image));
+    }
+}
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 2a59156..edd4256 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
@@ -85,6 +85,7 @@ import org.junit.runners.Suite;
     org.apache.sis.coverage.CategoryListTest.class,
     org.apache.sis.coverage.SampleDimensionTest.class,
     org.apache.sis.coverage.SampleRangeFormatTest.class,
+    org.apache.sis.internal.coverage.ImageUtilitiesTest.class,
     org.apache.sis.internal.coverage.ScaledColorSpaceTest.class,
     org.apache.sis.internal.coverage.BufferedGridCoverageTest.class,
     org.apache.sis.internal.coverage.GridCoverage2DTest.class,
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
index 23aeb70..cf576a6 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
@@ -122,6 +122,16 @@ public final class Vocabulary extends IndexedResourceBundle {
         public static final short BarometricAltitude = 13;
 
         /**
+         * Black
+         */
+        public static final short Black = 175;
+
+        /**
+         * Blue
+         */
+        public static final short Blue = 176;
+
+        /**
          * Cardinality
          */
         public static final short Cardinality = 14;
@@ -217,6 +227,11 @@ public final class Vocabulary extends IndexedResourceBundle {
         public static final short CurrentDirectory = 32;
 
         /**
+         * Cyan
+         */
+        public static final short Cyan = 177;
+
+        /**
          * Cycle omitted
          */
         public static final short CycleOmitted = 33;
@@ -407,6 +422,16 @@ public final class Vocabulary extends IndexedResourceBundle {
         public static final short GeographicIdentifier = 70;
 
         /**
+         * Gray
+         */
+        public static final short Gray = 178;
+
+        /**
+         * Green
+         */
+        public static final short Green = 179;
+
+        /**
          * Grid extent
          */
         public static final short GridExtent = 71;
@@ -527,6 +552,11 @@ public final class Vocabulary extends IndexedResourceBundle {
         public static final short LowerBound = 94;
 
         /**
+         * Magenta
+         */
+        public static final short Magenta = 180;
+
+        /**
          * Mandatory
          */
         public static final short Mandatory = 95;
@@ -692,6 +722,11 @@ public final class Vocabulary extends IndexedResourceBundle {
         public static final short Read = 127;
 
         /**
+         * Red
+         */
+        public static final short Red = 181;
+
+        /**
          * Remarks
          */
         public static final short Remarks = 128;
@@ -822,6 +857,11 @@ public final class Vocabulary extends IndexedResourceBundle {
         public static final short TransformationAccuracy = 152;
 
         /**
+         * Transparency
+         */
+        public static final short Transparency = 183;
+
+        /**
          * Truncated Julian
          */
         public static final short TruncatedJulian = 153;
@@ -925,6 +965,11 @@ public final class Vocabulary extends IndexedResourceBundle {
          * Write
          */
         public static final short Write = 173;
+
+        /**
+         * Yellow
+         */
+        public static final short Yellow = 182;
     }
 
     /**
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
index 56682fe..89741cf 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
@@ -27,6 +27,8 @@ AxisChanges             = Axis changes
 Azimuth                 = Azimuth
 Band_1                  = Band {0}
 BarometricAltitude      = Barometric altitude
+Black                   = Black
+Blue                    = Blue
 Cardinality             = Cardinality
 CausedBy_1              = Caused by {0}
 CellCount_1             = {0} cells
@@ -47,6 +49,7 @@ Create                  = Create
 CurrentDateTime         = Current date and time
 CurrentDirectory        = Current directory
 CycleOmitted            = Cycle omitted
+Cyan                    = Cyan
 DataBase                = Database
 DataDirectory           = Data directory
 DataFormats             = Data formats
@@ -84,6 +87,8 @@ GeodesicDistance        = Geodesic distance
 GeodeticDataset         = Geodetic dataset
 GeographicExtent        = Geographic extent
 GeographicIdentifier    = Geographic identifier
+Gray                    = Gray
+Green                   = Green
 GridExtent              = Grid extent
 Height                  = Height
 Identifier              = Identifier
@@ -108,6 +113,7 @@ Localization            = Localization
 LocationType            = Location type
 Logging                 = Logging
 LowerBound              = Lower bound
+Magenta                 = Magenta
 Mandatory               = Mandatory
 Mapping                 = Mapping
 MaximumValue            = Maximum value
@@ -141,6 +147,7 @@ Plugins                 = Plug-ins
 Preprocessing           = Preprocessing
 Quoted_1                = \u201c{0}\u201d
 Read                    = Read
+Red                     = Red
 Remarks                 = Remarks
 RemoteConfiguration     = Remote configuration
 RepresentativeValue     = Representative value
@@ -167,6 +174,7 @@ Time_1                  = {0} time
 Timezone                = Timezone
 Transformation          = Transformation
 TransformationAccuracy  = Transformation accuracy
+Transparency            = Transparency
 TruncatedJulian         = Truncated Julian
 Type                    = Type
 Unknown                 = Unknown
@@ -188,3 +196,4 @@ Warnings                = Warnings
 WestBound               = West bound
 World                   = World
 Write                   = Write
+Yellow                  = Yellow
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
index 0e80ee4..e9e6323 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
@@ -34,6 +34,8 @@ AxisChanges             = Changements d\u2019axes
 Azimuth                 = Azimut
 Band_1                  = Bande {0}
 BarometricAltitude      = Altitude barom\u00e9trique
+Black                   = Noir
+Blue                    = Bleu
 Cardinality             = Cardinalit\u00e9
 CausedBy_1              = Caus\u00e9e par {0}
 CellCount_1             = {0} cellules
@@ -53,6 +55,7 @@ CoverageDomain          = Domaine de la couverture de donn\u00e9es
 Create                  = Cr\u00e9ation
 CurrentDateTime         = Date et heure courantes
 CurrentDirectory        = R\u00e9pertoire courant
+Cyan                    = Cyan
 CycleOmitted            = Cycle omit
 DataBase                = Base de donn\u00e9es
 DataDirectory           = R\u00e9pertoire des donn\u00e9es
@@ -91,6 +94,8 @@ GeodesicDistance        = Distance g\u00e9od\u00e9sique
 GeodeticDataset         = Base de donn\u00e9es g\u00e9od\u00e9sique
 GeographicExtent        = \u00c9tendue g\u00e9ographique
 GeographicIdentifier    = Identifiant g\u00e9ographique
+Gray                    = Gris
+Green                   = Vert
 GridExtent              = \u00c9tendue de la grille
 Height                  = Hauteur
 Identifier              = Identifiant
@@ -115,6 +120,7 @@ Localization            = R\u00e9gionalisation
 LocationType            = Type de location
 Logging                 = Journalisation
 LowerBound              = Limite basse
+Magenta                 = Magenta
 Mandatory               = Requis
 Mapping                 = Cartographie
 MaximumValue            = Valeur maximale
@@ -148,6 +154,7 @@ Plugins                 = Modules d\u2019extension
 Preprocessing           = Pr\u00e9traitement
 Quoted_1                = \u00ab\u202f{0}\u202f\u00bb
 Read                    = Lecture
+Red                     = Rouge
 Remarks                 = Remarques
 RemoteConfiguration     = Configuration distante
 RepresentativeValue     = Valeur repr\u00e9sentative
@@ -174,6 +181,7 @@ Time_1                  = Heure {0}
 Timezone                = Fuseau horaire
 Transformation          = Transformation
 TransformationAccuracy  = Pr\u00e9cision de la transformation
+Transparency            = Transparence
 TruncatedJulian         = Julien tronqu\u00e9
 Type                    = Type
 Unknown                 = Inconnu
@@ -195,3 +203,4 @@ Warnings                = Avertissements
 WestBound               = Limite ouest
 World                   = Monde
 Write                   = \u00c9criture
+Yellow                  = Jaune


Mime
View raw message