/* * 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.sentinel2; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.util.Collection; import java.util.Iterator; import javax.imageio.spi.IIORegistry; import javax.imageio.spi.ImageReaderSpi; import javax.media.jai.ImageLayout; import javax.media.jai.InterpolationNearest; import javax.media.jai.JAI; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderedImageAdapter; import javax.media.jai.RenderedOp; import com.sun.media.imageioimpl.plugins.jpeg2000.J2KImageReaderSpi; import fr.gael.drb.cortex.topic.sentinel2.DrbImageManager.Bands; import fr.gael.drbx.image.DrbCollectionImage; import fr.gael.drbx.image.DrbImage; import java.awt.image.SampleModel; /** * This class manage all the sentinel-2 images processing * */ public class Sentinel2Image { // Be sure to not use default buggy JP2K driver. static { try { IIORegistry registry = IIORegistry.getDefaultInstance(); ImageReaderSpi spi = registry.getServiceProviderByClass( J2KImageReaderSpi.class); registry.deregisterServiceProvider(spi); } catch (Exception e) { // Cannot unregister default JP2K reader. } } /** * Processes rendering of a Sentinel-2 1C Tile list passed as parameter. * @param input the input image collection * @param bands number of interleaved physical files representing a bands. * if band>1, consecutive images are considered as bands, * It in case of preview RGB images all the bands are in one physical band, * in this case, band=1. * @param horizontal_padding padding between horizontal assembled images. * @param vertical_padding padding between vertical assembled images. * @return the rendered and mosaic'd image. */ public static RenderedImage processLocatedImage (RenderedImageAdapter input, int bands, int horizontal_padding, int vertical_padding) { DrbCollectionImage source = (DrbCollectionImage)input.getWrappedImage(); return processLocatedImage (source, bands, horizontal_padding, vertical_padding); } public static RenderedImage processLocatedImage (DrbCollectionImage source, int bands, int horizontal_padding, int vertical_padding) { // Prepare output mosaic layout ImageLayout layout = new ImageLayout(); boolean isLayoutTileSet = false; // Prepare variables for building output strip mosaic int image_width=source.getWidth(); int image_height=source.getHeight(); ParameterBlockJAI mosaicParameters = new ParameterBlockJAI("Mosaic", "rendered"); mosaicParameters.setParameter("mosaicType", javax.media.jai.operator.MosaicDescriptor.MOSAIC_TYPE_BLEND); Collection<DrbImage>images = source.getChildren(); DrbImageManager im = new DrbImageManager(); im.addAll (images); // Computes the mosaic Bands[][] rows = im.getMosaic(); RenderedImage current_image = null; int row_number = rows.length; // Loop on image table for (int irow=1; irow<row_number; irow++) { int column_number = rows[irow].length; for (int icol=1; icol<column_number; icol++) { Bands banded_images_map=rows[irow][icol]; // Look at the band composition // Case of no image at this position: shift to the next image if (banded_images_map==null) { current_image = new BufferedImage(image_width, image_height, BufferedImage.TYPE_3BYTE_BGR); } else if (banded_images_map.values().size()>1) { ParameterBlock pb = new ParameterBlock(); for (DrbImage image:banded_images_map.values()) { ParameterBlock fmt_pb = new ParameterBlock(); fmt_pb.addSource(image); fmt_pb.add(DataBuffer.TYPE_USHORT); RenderedOp op = JAI.create("Format", fmt_pb); pb.addSource(op); } current_image = JAI.create("bandMerge", pb); } else { // Case of 1 band image or RGB //Probably PVI image current_image = banded_images_map.values().iterator().next(); } // Set layout tile size if not already done: assuming all the // images has the same size. if (!isLayoutTileSet) { layout.setTileWidth(current_image.getTileWidth()); layout.setTileHeight(current_image.getTileHeight()); layout.setColorModel(current_image.getColorModel()); layout.setSampleModel(current_image.getSampleModel()); isLayoutTileSet = true; } image_width = current_image.getWidth(); image_height = current_image.getHeight(); // Translate strip to the output coordinate (vertical shift) ParameterBlock translateParameters = new ParameterBlock(); translateParameters.addSource(current_image); translateParameters.add((float)(vertical_padding+image_width)*(icol-1)); translateParameters.add((float)(horizontal_padding+image_height)*(irow-1)); translateParameters.add(new InterpolationNearest()); current_image = JAI.create("translate", translateParameters, new RenderingHints(JAI.KEY_IMAGE_LAYOUT,layout)); // Add current strip to the output mosaic mosaicParameters.addSource(current_image); // Go to the next image } } // Initialize background value double [] backgroundValues = new double [bands]; for (int j = 0; j < bands; j++) backgroundValues[j] = 0.0D; mosaicParameters.setParameter("backgroundValues", backgroundValues); // Create output mosaic RenderedOp finalImage = JAI.create("mosaic", mosaicParameters, new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout)); // Convert dataBuffer SampleModel sm = finalImage.getSampleModel (); if (sm.getDataType () != DataBuffer.TYPE_BYTE) { ParameterBlock pb = new ParameterBlock (); pb.addSource (finalImage); pb.add (DataBuffer.TYPE_BYTE); finalImage = JAI.create ("format", pb); } return finalImage; } /** * Processes rendering of a Sentinel-2 1B Tile list passed as parameter. * @param input the input image collection * @param bands number of interleaved physical files representing a bands. * if band>1, consecutive images are considered as bands, * It in case of preview RGB images all the bands are in one physical band, * in this case, band=1. * @param horizontal_padding padding between horizontal assembled images. * @param vertical_padding padding between vertical assembled images. * @return the rendered and mosaic'd image. */ public static RenderedImage process1BImage (RenderedImageAdapter input, int bands, int horizontal_padding, int vertical_padding) { DrbCollectionImage source = (DrbCollectionImage)input.getWrappedImage(); return process1BImage (source, bands, horizontal_padding, vertical_padding); } public static RenderedImage process1BImage (DrbCollectionImage source, int bands, int horizontal_padding, int vertical_padding) { // Prepare output mosaic layout ImageLayout layout = new ImageLayout(); boolean isLayoutTileSet = false; // Prepare variables for building output strip mosaic int currentWidth = horizontal_padding; int currentHeight = vertical_padding; ParameterBlockJAI mosaicParameters = new ParameterBlockJAI("Mosaic", "rendered"); mosaicParameters.setParameter("mosaicType", javax.media.jai.operator.MosaicDescriptor.MOSAIC_TYPE_BLEND); Collection<DrbImage>images = source.getChildren(); Iterator<DrbImage> image_it = images.iterator(); while (image_it.hasNext()) { RenderedImage current_image = null; // Select the working bands ParameterBlock pb = new ParameterBlock(); DrbImage fmt = null; if (bands>1) { for (int i=0; i<bands; i++) { fmt = image_it.next(); ParameterBlock fmt_pb = new ParameterBlock(); fmt_pb.addSource(fmt); fmt_pb.add(DataBuffer.TYPE_BYTE); RenderedOp op = JAI.create("Format", fmt_pb); pb.addSource(op); } current_image = JAI.create("bandMerge", pb); } else { //Probably PVI image current_image = image_it.next(); } // Set layout tile size if not already done if (!isLayoutTileSet) { layout.setTileWidth(current_image.getTileWidth()); layout.setTileHeight(current_image.getTileHeight()); layout.setColorModel(current_image.getColorModel()); layout.setSampleModel(current_image.getSampleModel()); isLayoutTileSet = true; } // Translate strip to the output coordinate (vertical shift) ParameterBlock translateParameters = new ParameterBlock(); translateParameters.addSource(current_image); translateParameters.add((float) currentWidth); translateParameters.add((float) currentHeight); translateParameters.add(new InterpolationNearest()); current_image = JAI.create("translate", translateParameters, new RenderingHints(JAI.KEY_IMAGE_LAYOUT,layout)); // TODO: find a way to retrieves the granules position within // the mosaic. // Update following strip translation /* if ((source_index%13)==0) {*/ currentWidth=horizontal_padding; currentHeight += current_image.getHeight() + vertical_padding; /* } else { currentWidth += current_image.getWidth() + horizontal_padding; }*/ // Add current strip to the output mosaic mosaicParameters.addSource(current_image); // Go to the next image } double [] backgroundValues = new double [bands]; for (int j = 0; j < bands; j++) { backgroundValues[j] = 0.0D; } mosaicParameters.setParameter("backgroundValues", backgroundValues); // Create output mosaic return JAI.create("mosaic", mosaicParameters, new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout)); } }