Search in sources :

Example 6 with ImageStatistics

use of org.geotoolkit.storage.coverage.ImageStatistics in project geotoolkit by Geomatys.

the class GO2Utilities method inferStyle.

/**
 * Try to create a simple style from given statistics. For now, two cases exists:
 * <ol>
 *     <li>
 *         Less than 3 bands are detected. In this case, we simply create a color map over first band. The
 *         color map make use of standard deviation to compute interpolation boundaries.
 *     </li>
 *     <li>
 *         For 3 bands or more, we create a dynamic range RGB rendering based on first bands in the statistics. We
 *         define value boundary for each band using {@link #buildRanges(ImageStatistics, int) a custom empiric
 *         method removing extremums}.
 *     </li>
 * </ol>
 * @param stats The statistics to use for style creation. Can be null or empty, in which case nothing is returned.
 * @param forceAlpha A flag used only if given statistics contains more than 3 bands. Indicates that for RGB styling,
 *                   we must interpret 4th band as alpha component.
 * @return A style inferred from input statistics, or an empty shell if given argument does not contains enough
 * information.
 * @throws IOException It can happen when trying to access a color map definition on disk, but it fails.
 */
public static Optional<MutableStyle> inferStyle(final ImageStatistics stats, final boolean forceAlpha) throws IOException {
    if (stats == null)
        return Optional.empty();
    final ImageStatistics.Band[] bands = stats.getBands();
    if (bands == null || bands.length < 1)
        return Optional.empty();
    if (bands.length < 3) {
        LOGGER.log(Level.FINE, "applyColorMapStyle : fallBack way is choosen." + "GrayScale interpretation of the first coverage image band.");
        final ImageStatistics.Band band0 = bands[0];
        final Double bmin = band0.getMin();
        final Double bmax = band0.getMax();
        if (bmin == null || bmax == null || !Double.isFinite(bmin) || !Double.isFinite(bmax)) {
            LOGGER.log(Level.WARNING, "Image statistics object is present but contains null or non-finite extremas. Ignore it");
            return Optional.empty();
        }
        double palMin = bmin;
        double palMax = bmax;
        final Double mean = band0.getMean();
        final Double std = band0.getStd();
        if (mean != null && std != null && Double.isFinite(mean) && Double.isFinite(std)) {
            palMin = Math.max(bmin, mean - 2 * std);
            palMax = Math.min(bmax, mean + 2 * std);
        }
        if (!Double.isFinite(palMin) || !Double.isFinite(palMax)) {
            LOGGER.finest("Adapting rendering distribution failed. Fallback on input min/max");
            palMin = bmin;
            palMax = bmax;
        }
        assert Double.isFinite(palMin) : "Raster Style fallback : minimum value should be finite. min = " + palMin;
        assert Double.isFinite(palMax) : "Raster Style fallback : maximum value should be finite. max = " + palMax;
        assert palMin >= bmin;
        assert palMax <= bmax;
        final List<InterpolationPoint> values = new ArrayList<>();
        final double[] nodatas = band0.getNoData();
        if (nodatas != null)
            for (double nodata : nodatas) {
                values.add(STYLE_FACTORY.interpolationPoint(nodata, STYLE_FACTORY.literal(new Color(0, 0, 0, 0))));
            }
        values.add(STYLE_FACTORY.interpolationPoint(Float.NaN, STYLE_FACTORY.literal(new Color(0, 0, 0, 0))));
        // -- Color palette
        // Color[] colorsPal = PaletteFactory.getDefault().getColors("rainbow-t");
        Color[] colorsPal = PaletteFactory.getDefault().getColors("grayscale");
        assert colorsPal.length >= 2;
        if (colorsPal.length < 4) {
            final double percent_5 = (colorsPal.length == 3) ? 0.1 : 0.05;
            final Color[] colorsPalTemp = colorsPal;
            colorsPal = Arrays.copyOf(colorsPal, colorsPal.length + 2);
            System.arraycopy(colorsPalTemp, 2, colorsPal, 2, colorsPalTemp.length - 2);
            colorsPal[colorsPal.length - 1] = colorsPalTemp[colorsPalTemp.length - 1];
            colorsPal[1] = DefaultInterpolate.interpolate(colorsPalTemp[0], colorsPalTemp[1], percent_5);
            colorsPal[colorsPal.length - 2] = DefaultInterpolate.interpolate(colorsPalTemp[colorsPalTemp.length - 2], colorsPalTemp[colorsPalTemp.length - 1], 1 - percent_5);
        }
        // -- if difference between band minimum statistic and palette minimum,
        // -- define values between them as transparency
        values.add(STYLE_FACTORY.interpolationPoint(bmin, STYLE_FACTORY.literal(colorsPal[0])));
        assert colorsPal.length >= 4;
        // -- min and max transparency
        final double step = (palMax - palMin) / (colorsPal.length - 3);
        double currentVal = palMin;
        for (int c = 1; c <= colorsPal.length - 2; c++) {
            values.add(STYLE_FACTORY.interpolationPoint(currentVal, STYLE_FACTORY.literal(colorsPal[c])));
            currentVal += step;
        }
        assert StrictMath.abs(currentVal - step - palMax) < 1E-9;
        values.add(STYLE_FACTORY.interpolationPoint(bmax, STYLE_FACTORY.literal(colorsPal[colorsPal.length - 1])));
        final Expression function = STYLE_FACTORY.interpolateFunction(DEFAULT_CATEGORIZE_LOOKUP, values, Method.COLOR, Mode.LINEAR, DEFAULT_FALLBACK);
        final ColorMap colorMap = STYLE_FACTORY.colorMap(function);
        final RasterSymbolizer symbol = STYLE_FACTORY.rasterSymbolizer(band0.getName(), null, null, null, colorMap, null, null, null);
        return Optional.of(STYLE_FACTORY.style(symbol));
    } else {
        LOGGER.log(Level.FINE, "RGBStyle : fallBack way is choosen." + "RGB interpretation of the three first coverage image bands.");
        final int rgbNumBand = forceAlpha && bands.length > 3 ? 4 : 3;
        assert rgbNumBand <= bands.length;
        final double[][] ranges = buildRanges(stats, 4);
        final List<DRChannel> channels = new ArrayList<>();
        for (int i = 0; i < rgbNumBand; i++) {
            final DRChannel channel = new DRChannel();
            final String bandName = bands[i].getName();
            channel.setBand(bandName == null || bandName.isEmpty() ? Integer.toString(i) : bandName);
            channel.setColorSpaceComponent(DynamicRangeSymbolizer.DRChannel.RGBA_COMPONENTS[i]);
            DynamicRangeSymbolizer.DRBound drBound = new DynamicRangeSymbolizer.DRBound();
            drBound.setValue(FILTER_FACTORY.literal(ranges[i][0]));
            channel.setLower(drBound);
            drBound = new DynamicRangeSymbolizer.DRBound();
            drBound.setValue(FILTER_FACTORY.literal(ranges[i][1]));
            channel.setUpper(drBound);
            channels.add(channel);
        }
        final DynamicRangeSymbolizer drgb = new DynamicRangeSymbolizer();
        drgb.setChannels(channels);
        return Optional.of(STYLE_FACTORY.style(drgb));
    }
}
Also used : InterpolationPoint(org.geotoolkit.style.function.InterpolationPoint) DRChannel(org.geotoolkit.display2d.ext.dynamicrange.DynamicRangeSymbolizer.DRChannel) Color(java.awt.Color) ArrayList(java.util.ArrayList) MultiLineString(org.locationtech.jts.geom.MultiLineString) LineString(org.locationtech.jts.geom.LineString) MultiPoint(org.locationtech.jts.geom.MultiPoint) Point(org.locationtech.jts.geom.Point) Paint(java.awt.Paint) InterpolationPoint(org.geotoolkit.style.function.InterpolationPoint) DynamicRangeSymbolizer(org.geotoolkit.display2d.ext.dynamicrange.DynamicRangeSymbolizer) Expression(org.opengis.filter.Expression) ImageStatistics(org.geotoolkit.storage.coverage.ImageStatistics)

Example 7 with ImageStatistics

use of org.geotoolkit.storage.coverage.ImageStatistics in project geotoolkit by Geomatys.

the class RasterSymbolizerRenderer method applyColorMapStyle.

/**
 * Apply {@linkplain RasterSymbolizer#getColorMap() color map style properties} on current coverage if need.<br><br>
 *
 * In case where no {@linkplain ColorMap#getFunction() sample to geophysic}
 * transformation function is available and coverage is define as {@link ViewType#GEOPHYSICS}
 * a way is find to avoid empty result, like follow : <br>
 * The first band from {@linkplain GridCoverage2D#getRenderedImage() coverage image} is selected
 * and a grayscale color model is apply from {@linkplain ImageStatistics computed image statistic}.
 *
 * @param ref needed to compute statistics from internal metadata in case where missing informations.
 * @param coverage color map style apply on this object.
 * @param styleElement the {@link RasterSymbolizer} which contain color map properties.
 * @return image which is the coverage exprimate into {@link ViewType#PHOTOGRAPHIC}.
 * @throws ProcessException if problem during statistic problem.
 */
private static RenderedImage applyColorMapStyle(final GridCoverageResource ref, GridCoverage coverage, final RasterSymbolizer styleElement) throws ProcessException, IOException, PortrayalException {
    ensureNonNull("CoverageReference", ref);
    ensureNonNull("coverage", coverage);
    ensureNonNull("styleElement", styleElement);
    RenderedImage resultImage;
    // Recolor coverage -----------------------------------------------------
    final ColorMap recolor = styleElement.getColorMap();
    recolorCase: if (recolor == null || recolor.getFunction() == null) {
        resultImage = coverage.forConvertedValues(false).render(null);
        final SampleModel sampleMod = resultImage.getSampleModel();
        final ColorModel riColorModel = resultImage.getColorModel();
        /**
         * Break computing statistic if indexcolormodel is already adapted for java 2d interpretation
         * (which mean index color model with positive colormap array index -> DataBuffer.TYPE_BYTE || DataBuffer.TYPE_USHORT)
         * or if image has already 3 or 4 bands Byte typed.
         */
        if (riColorModel != null && !defaultStyleIsNeeded(sampleMod, riColorModel)) {
            break recolorCase;
        }
        // if there is no geophysic, the same coverage is returned
        coverage = coverage.forConvertedValues(true);
        CoverageDescription covRefMetadata = null;
        if (covRefMetadata == null) {
            final Metadata metadata;
            try {
                metadata = ref.getMetadata();
            } catch (DataStoreException ex) {
                throw new IOException("Cannot fetch metadata from input resource.", ex);
            }
            covRefMetadata = MetadataUtilities.extractCoverageDescription(metadata).findFirst().orElse(null);
        }
        ImageStatistics analyse = null;
        if (covRefMetadata != null) {
            analyse = ImageStatistics.transform(covRefMetadata);
            if (analyse != null) {
                // Ensure band statistics are valid.
                for (ImageStatistics.Band b : analyse.getBands()) {
                    if (b.getMax() == null || b.getMin() == null || b.getMax() - b.getMin() < 1e-11) {
                        analyse = null;
                        break;
                    }
                }
            }
        }
        // ensure consistency over tiled rendering (cf. OpenLayer/WMS).
        if (analyse == null) {
            analyse = Statistics.analyse(coverage.render(null), true);
        }
        final Optional<MutableStyle> styleFromStats = GO2Utilities.inferStyle(analyse, (riColorModel == null) ? true : riColorModel.hasAlpha());
        if (styleFromStats.isPresent()) {
            /* WARNING: That's neither optimal nor stable. However, do not know any other way to override style on
                 * the fly.
                 *
                 * !!! IMPORTANT !!!
                 * The canvas here is created with the geometry of input coverage, because otherwise, we would apply
                 * two times the affine transform to display system.
                 */
            final MapLayers subCtx = MapBuilder.createContext();
            subCtx.getComponents().add(MapBuilder.createCoverageLayer(coverage, styleFromStats.get()));
            resultImage = DefaultPortrayalService.portray(new CanvasDef(coverage.getGridGeometry()), new SceneDef(subCtx));
        }
    } else {
        // color map is applied on geophysics view
        // if there is no geophysic, the same coverage is returned
        coverage = coverage.forConvertedValues(true);
        resultImage = (RenderedImage) recolor.getFunction().apply(coverage.render(null));
    }
    assert resultImage != null : "applyColorMapStyle : image can't be null.";
    return resultImage;
}
Also used : DataStoreException(org.apache.sis.storage.DataStoreException) Optional(java.util.Optional) ColorMap(org.opengis.style.ColorMap) Metadata(org.opengis.metadata.Metadata) CoverageDescription(org.opengis.metadata.content.CoverageDescription) IOException(java.io.IOException) SampleModel(java.awt.image.SampleModel) IndexColorModel(java.awt.image.IndexColorModel) ComponentColorModel(java.awt.image.ComponentColorModel) ColorModel(java.awt.image.ColorModel) ImageStatistics(org.geotoolkit.storage.coverage.ImageStatistics) SceneDef(org.geotoolkit.display2d.service.SceneDef) WritableRenderedImage(java.awt.image.WritableRenderedImage) RenderedImage(java.awt.image.RenderedImage) CanvasDef(org.geotoolkit.display2d.service.CanvasDef) MapLayers(org.apache.sis.portrayal.MapLayers)

Aggregations

ImageStatistics (org.geotoolkit.storage.coverage.ImageStatistics)7 Paint (java.awt.Paint)2 RenderedImage (java.awt.image.RenderedImage)2 SampleModel (java.awt.image.SampleModel)2 InterpolationPoint (org.geotoolkit.style.function.InterpolationPoint)2 Test (org.junit.Test)2 MultiPoint (org.locationtech.jts.geom.MultiPoint)2 Point (org.locationtech.jts.geom.Point)2 Color (java.awt.Color)1 ColorModel (java.awt.image.ColorModel)1 ComponentColorModel (java.awt.image.ComponentColorModel)1 IndexColorModel (java.awt.image.IndexColorModel)1 Raster (java.awt.image.Raster)1 WritableRenderedImage (java.awt.image.WritableRenderedImage)1 IOException (java.io.IOException)1 ArrayList (java.util.ArrayList)1 Optional (java.util.Optional)1 DoublePredicate (java.util.function.DoublePredicate)1 SampleDimension (org.apache.sis.coverage.SampleDimension)1 GridCoverage (org.apache.sis.coverage.grid.GridCoverage)1