fr.gael.drb.cortex.topic.sentinel3.jai.operator.QuicklookSlstrRIF.java Source code

Java tutorial

Introduction

Here is the source code for fr.gael.drb.cortex.topic.sentinel3.jai.operator.QuicklookSlstrRIF.java

Source

/*
 * Data HUb Service (DHuS) - For Space data distribution.
 * Copyright (C) 2013,2014,2015,2016 European Space Agency (ESA)
 * Copyright (C) 2013,2014,2015,2016 GAEL Systems
 * Copyright (C) 2013,2014,2015,2016 Serco Spa
 *
 * This file is part of DHuS software sources.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package fr.gael.drb.cortex.topic.sentinel3.jai.operator;

import java.awt.Color;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.ParameterBlock;
import java.awt.image.renderable.RenderedImageFactory;

import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
import org.apache.log4j.Logger;

import fr.gael.drb.cortex.topic.sentinel3.jai.operator.Common.PixelCorrection;
import fr.gael.drbx.image.DrbImage;

/**
 * This render image factory is dedicated to the preparation of the OLCI 
 * quicklook operator.
 */
public class QuicklookSlstrRIF implements RenderedImageFactory {
    private static Logger LOGGER = Logger.getLogger(QuicklookSlstrRIF.class);

    /**
     * Should create a new instance of <code>QuicklookSlstrOpImage</code> in the 
     * rendered layer.
     * This operator could be called by chunks of images.
     * A set of additional information are required to compute the pixels 
     * adjustment such as sun azimuth/elevation and detectors... The methods to
     * extract these informations are also provided here before. 
     * 
     * @param paramBlock The three R/G/B sources images to be "Merged" together
     * to produce the Quicklook.
     * @param renderHints Optionally contains destination image layout.
     */
    public RenderedImage create(ParameterBlock paramBlock, RenderingHints hints) {
        long start = System.currentTimeMillis();
        RenderedImage computed_image = null;

        DrbImage red = (DrbImage) paramBlock.getSource(4); // S5
        DrbImage green = (DrbImage) paramBlock.getSource(2); // S3
        DrbImage blue = (DrbImage) paramBlock.getSource(1); // S2

        PixelCorrection[] pc = (PixelCorrection[]) paramBlock.getObjectParameter(0);
        PixelCorrection red_correction = pc != null ? pc[0] : null;
        PixelCorrection green_correction = pc != null ? pc[1] : null;
        PixelCorrection blue_correction = pc != null ? pc[2] : null;

        try {
            computed_image = naturalColors(red.getData(), red_correction, green.getData(), green_correction,
                    blue.getData(), blue_correction);
        } catch (Exception e) {
            // Image access problem: try to reprocess this other bands
            LOGGER.info("Natural color band looks bad. Trying S8...");
            DrbImage image = (DrbImage) paramBlock.getSource(7); // S8
            PixelCorrection corr = pc != null ? pc[3] : null;
            try {
                computed_image = grayScaleBand(image.getData(), corr, true);
            } catch (Exception e1) {
                // S8 also bad band: try with S9...
                LOGGER.info("Thermal band S8 looks bad. Trying S9...");
                image = (DrbImage) paramBlock.getSource(8); // S9
                corr = pc != null ? pc[4] : null;
                try {
                    computed_image = grayScaleBand(image.getData(), corr, false);
                } catch (Exception e2) {
                    throw new UnsupportedOperationException("Image cannot be processed (" + e1.getMessage() + ").",
                            e2);
                }
            }
        }

        LOGGER.info("Quicklook generated in " + (System.currentTimeMillis() - start) / 1000 + " secs");

        return computed_image;
    }

    /**
     * Compute the natural color QL from passed images. This method only 
     * assembles provided images as red/green/blue 8 bits channels.
     * @param red red channel image
     * @param green green channel image
     * @param blue blue channel image
     * @return the assembled image.
     */
    private RenderedImage naturalColors(Raster red, PixelCorrection rc, Raster green, PixelCorrection gc,
            Raster blue, PixelCorrection bc) {

        BufferedImage bred = toGrayScale(red, rc, false, false);
        BufferedImage bgreen = toGrayScale(green, gc, false, false);
        BufferedImage bblue = toGrayScale(blue, bc, false, false);

        BufferedImage quicklook = new BufferedImage(red.getWidth(), red.getHeight(), BufferedImage.TYPE_INT_RGB);

        for (int j = 0; j < red.getHeight(); j++) {
            for (int i = 0; i < red.getWidth(); i++) {
                int cred = new Color(bred.getRGB(i, j)).getRed();
                int cgreen = new Color(bgreen.getRGB(i, j)).getGreen();
                int cblue = new Color(bblue.getRGB(i, j)).getBlue();

                quicklook.setRGB(i, j, new Color(cred, cgreen, cblue).getRGB());
            }
        }

        return quicklook;
    }

    private BufferedImage toGrayScale(Raster in, PixelCorrection c, boolean invertColors, boolean ignoreBadStats) {
        int width = in.getWidth();
        int height = in.getHeight();
        // compute stats
        SummaryStatistics stats = new SummaryStatistics();
        for (int j = 0; j < height; j++) {
            for (int i = 0; i < width; i++) {
                int pixel = checkAndApplyCorrection(in.getSample(i, j, 0), c);
                if (pixel != c.nodata)
                    stats.addValue(pixel);
            }
        }
        double lowerBound = Math.max(stats.getMin(), stats.getMean() - 3 * stats.getStandardDeviation());
        double upperBound = Math.min(stats.getMax(), stats.getMean() + 3 * stats.getStandardDeviation());

        if (!ignoreBadStats)
            if (Double.isNaN(stats.getMean()) || Double.isNaN(stats.getStandardDeviation())
                    || stats.getStandardDeviation() < 1)
                throw new IllegalStateException("Ugly band stats. Acquired during night?");

        return toGrayScale(in, c, invertColors, lowerBound, upperBound);
    }

    private BufferedImage toGrayScale(Raster in, PixelCorrection c, boolean invertColors, double lowerBound,
            double upperBound) {
        double offset = -lowerBound;
        double scaleFactor = 256. / (upperBound - lowerBound);
        int width = in.getWidth();
        int height = in.getHeight();

        // generate
        BufferedImage out = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
        for (int j = 0; j < height; j++) {
            for (int i = 0; i < width; i++) {
                int pixel = checkAndApplyCorrection(in.getSample(i, j, 0), c);
                if (pixel == c.nodata) {
                    if (invertColors)
                        out.setRGB(i, j, new Color(255, 255, 255).getRGB());
                    else
                        out.setRGB(i, j, new Color(0, 0, 0).getRGB());
                    continue;
                }

                double normalized = (pixel + offset) * scaleFactor;
                int gray = (int) (Math.max(0, Math.min(255, normalized)));
                if (invertColors)
                    gray = 255 - gray;
                out.setRGB(i, j, new Color(gray, gray, gray).getRGB());
            }
        }
        return out;
    }

    int checkAndApplyCorrection(int pixel, PixelCorrection c) {
        float p = (float) pixel;
        // No correction to apply
        if (c == null)
            return pixel;
        // NODATA ???
        if (pixel == c.nodata)
            return c.nodata;
        if (pixel < 0)
            pixel += (2 * (Short.MAX_VALUE + 1));

        return (int) (p * c.scale + c.offset);
    }

    private RenderedImage grayScaleBand(Raster band, PixelCorrection c, boolean ignoreBadStats) {
        return toGrayScale(band, c, true, ignoreBadStats);
    }

}