/*
* ImageI/O-Ext - OpenSource Java Image translation Library
* http://www.geo-solutions.it/
* http://java.net/projects/imageio-ext/
* (C) 2007 - 2009, GeoSolutions
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* either version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*/
package it.geosolutions.imageio.matfile5.sas;
import it.geosolutions.imageio.matfile5.MatFileImageReader;
import it.geosolutions.imageio.matfile5.sas.SASTileMetadata.Channel;
import it.geosolutions.imageio.utilities.ImageIOUtilities;
import it.geosolutions.imageio.utilities.Utilities;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.image.BandedSampleModel;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferDouble;
import java.awt.image.DataBufferFloat;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadata;
import com.jmatio.io.MatFileFilter;
import com.jmatio.io.MatFileReader;
import com.jmatio.types.MLArray;
import com.jmatio.types.MLDouble;
import com.jmatio.types.MLNumericArray;
/**
*
* @author Daniele Romagnoli, GeoSolutions SAS
*
*/
public class SASTileImageReader extends MatFileImageReader {
public SASTileImageReader(SASTileImageReaderSpi originatingProvider) {
super(originatingProvider);
}
private final static boolean COMPUTE_LOGARITHM;
private final static boolean DISABLE_MEDIALIB_LOG;
static{
final String cl = System.getenv("SAS_COMPUTE_LOG");
final String disableMediaLog = System.getenv("DISABLE_MEDIALIB_LOG");
if (cl!=null && cl.trim().length()>0)
COMPUTE_LOGARITHM = Boolean.parseBoolean(cl);
else
COMPUTE_LOGARITHM = true;
if (disableMediaLog!=null && disableMediaLog.trim().length()>0)
DISABLE_MEDIALIB_LOG = Boolean.parseBoolean(disableMediaLog);
else
DISABLE_MEDIALIB_LOG = false;
if (DISABLE_MEDIALIB_LOG){
Utilities.setNativeAccelerationAllowed("Log",false);
}
}
private boolean isInitialized = false;
private SASTileMetadata sasTile = null;
protected synchronized void initialize() {
if (!isInitialized) {
final Object datainput = super.getInput();
final String fileName = getDatasetSource(datainput).getAbsolutePath();
final MatFileFilter filter = new MatFileFilter();
initFilter(filter, SASTileMetadata.getFilterElements());
try {
matReader = new MatFileReader(fileName, filter, true);
sasTile = new SASTileMetadata(matReader);
dataArrays = new LinkedList<String>();
dataArrays.add(sasTile.isLogScale() ? SASTileMetadata.SAS_TILE_LOG :SASTileMetadata.SAS_TILE_RAW);
} catch (IOException e) {
throw new RuntimeException("Unable to Initialize the reader", e);
}
}
isInitialized = true;
}
/**
* Returns the height of the raster.
*
* @param imageIndex
* the index of the specified raster
* @return raster height
*/
@Override
public int getHeight(int imageIndex) throws IOException {
initialize();
return sasTile.getYPixels();
}
/**
* Returns the width of the raster.
*
* @param imageIndex
* the index of the specified raster
* @return raster width
*/
@Override
public int getWidth(int imageIndex) throws IOException {
initialize();
return sasTile.getXPixels();
}
@Override
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
initialize();
return sasTile;
}
@Override
public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
initialize();
final int width = getWidth(imageIndex);
final int height = getHeight(imageIndex);
if (param == null)
param = getDefaultReadParam();
int dstWidth = -1;
int dstHeight = -1;
int srcRegionWidth = -1;
int srcRegionHeight = -1;
int srcRegionXOffset = -1;
int srcRegionYOffset = -1;
int xSubsamplingFactor = -1;
int ySubsamplingFactor = -1;
int xSubsamplingOffset = 0;
int ySubsamplingOffset = 0;
// //
//
// Retrieving Information about Source Region and doing
// additional initialization operations.
//
// //
Rectangle srcRegion = param.getSourceRegion();
if (srcRegion != null) {
srcRegionWidth = (int) srcRegion.getWidth();
srcRegionHeight = (int) srcRegion.getHeight();
srcRegionXOffset = (int) srcRegion.getX();
srcRegionYOffset = (int) srcRegion.getY();
// //
//
// Minimum correction for wrong source regions
//
// When you do sub-sampling or source sub-setting it might happen
// that the given source region in the read parameter is incorrect,
// which means it can be or a bit larger than the original file or
// can begin a bit before original limits.
//
// We got to be prepared to handle such case in order to avoid
// generating ArrayIndexOutOfBoundsException later in the code.
//
// //
if (srcRegionXOffset < 0)
srcRegionXOffset = 0;
if (srcRegionYOffset < 0)
srcRegionYOffset = 0;
if ((srcRegionXOffset + srcRegionWidth) > width) {
srcRegionWidth = width - srcRegionXOffset;
}
// initializing dstWidth
dstWidth = srcRegionWidth;
if ((srcRegionYOffset + srcRegionHeight) > height) {
srcRegionHeight = height - srcRegionYOffset;
}
// initializing dstHeight
dstHeight = srcRegionHeight;
} else {
// Source Region not specified.
// Assuming Source Region Dimension equal to Source Image Dimension
dstWidth = width;
dstHeight = height;
srcRegionXOffset = srcRegionYOffset = 0;
srcRegionWidth = width;
srcRegionHeight = height;
}
// SubSampling variables initialization
xSubsamplingFactor = param.getSourceXSubsampling();
ySubsamplingFactor = param.getSourceYSubsampling();
xSubsamplingOffset = param.getSubsamplingXOffset();
ySubsamplingOffset = param.getSubsamplingYOffset();
dstWidth = ((dstWidth - 1 - xSubsamplingOffset) / xSubsamplingFactor) + 1;
dstHeight = ((dstHeight - 1 - ySubsamplingOffset) / ySubsamplingFactor) + 1;
// ////////////////////////////////////////////////////////////////////
//
// Reading data
//
// ////////////////////////////////////////////////////////////////////
final Rectangle roi = new Rectangle(srcRegionXOffset, srcRegionYOffset, srcRegionWidth, srcRegionHeight);
final MLArray mlArrayRetrived = matReader.getMLArray(dataArrays.get(imageIndex));
final ByteBuffer real = ((MLNumericArray<Number>) mlArrayRetrived).getRealByteBuffer();
final ByteBuffer imaginary = ((MLNumericArray<Number>) mlArrayRetrived).getImaginaryByteBuffer();
final boolean isDouble = (mlArrayRetrived instanceof MLDouble)? true : false;
final int imageSize = width * height;
// //
//
// Note that the underlying matrix fill a buffer where samples are sorted as:
// First row, first column, second row, first column, third row, first column...
// Therefore I'm getting a transposed image. I will transpose it afterwards.
// Note that I'm building a Sample Model with height and width swapped.
//
// Numerical Example: The Matlab Matrix is 3X3 as:
//
// 1, 2, 3
// 4, 5, 6
// 7, 8, 9
//
// The DataBuffer will contains data as:
// 1, 4, 7, 2, 5, 8, 3, 6, 9
//
// //
final int smWidth = height;
final int smHeight = width;
final BandedSampleModel sampleModel = new BandedSampleModel(isDouble?DataBuffer.TYPE_DOUBLE:DataBuffer.TYPE_FLOAT, smWidth, smHeight, 2);
final ColorModel cm = ImageIOUtilities.createColorModel(sampleModel);
final WritableRaster originalRasterData;
if (isDouble){
final double[][]dataArray = new double[2][imageSize];
final DoubleBuffer buffReal = real.asDoubleBuffer();
buffReal.get(dataArray[0]);
final DoubleBuffer buffImaginary = imaginary.asDoubleBuffer();
buffImaginary.get(dataArray[1]);
final DataBufferDouble dbb = new DataBufferDouble(dataArray, imageSize);
originalRasterData = Raster.createWritableRaster(sampleModel,dbb, null);
} else {
final float[][] dataArray = new float[2][imageSize];
final FloatBuffer buffReal = real.asFloatBuffer();
buffReal.get(dataArray[0]);
final FloatBuffer buffImaginary = imaginary.asFloatBuffer();
buffImaginary.get(dataArray[1]);
final DataBufferFloat dbb = new DataBufferFloat(dataArray, imageSize);
originalRasterData = Raster.createWritableRaster(sampleModel,dbb, null);
}
BufferedImage data = new BufferedImage(cm, originalRasterData, false,null);
//
// CROP
//
if (srcRegion != null) {
//Transpose haven't been executed yet.
//the actual image has x-y swapped.
final int x = roi.y;
final int y = roi.x;
final int w = roi.height;
final int h = roi.width;
data = data.getSubimage(x, y, w, h);
}
//Tuneup the image read param to use the adjusted one
final ImageReadParam tunedParam = getDefaultReadParam();
tunedParam.setSourceSubsampling(xSubsamplingFactor, ySubsamplingFactor, xSubsamplingOffset, ySubsamplingOffset);
tunedParam.setSourceRegion(roi);
AffineTransform transform = getAffineTransform(tunedParam);
//
// apply the geometric transform
//
SASAffineTransformOp transformOp = new SASAffineTransformOp(transform, null);
BufferedImage dst = null;
dst = transformOp.filter(data, dst);
// //
//
// Computing the magnitude of the stored complex values.
//
// //
return new SASBufferedImageOp(COMPUTE_LOGARITHM, null).filter(dst,null);
}
@Override
protected AffineTransform getPreTransform(ImageReadParam param) throws IOException {
int xSubsamplingFactor = 1;
int ySubsamplingFactor = 1;
int height = getHeight(0);
int width = getWidth(0);
if (param!=null){
xSubsamplingFactor = param.getSourceXSubsampling();
ySubsamplingFactor = param.getSourceYSubsampling();
final Rectangle sourceRegion=param.getSourceRegion();
if (sourceRegion!=null){
width = sourceRegion.width;
height = sourceRegion.height;
}
}
final AffineTransform transform = AffineTransform.getRotateInstance(0);// identity
// //
//
// Tuning the Affine Transform
//
// //
Channel channel = sasTile.getChannel();
if (channel == Channel.STARBOARD){
//Vertical Flip
transform.preConcatenate(AffineTransform.getScaleInstance(1,-1));
//Do Translate before Flipping (uses subsampling and source region)
double ty = height;
if (ySubsamplingFactor!=1)
ty/=ySubsamplingFactor;
transform.preConcatenate(AffineTransform.getTranslateInstance(0,ty));
} else {
//Vertical+Horizontal Flip
transform.preConcatenate(AffineTransform.getScaleInstance(-1,-1));
//Do Translate before Flipping (uses subsampling and source region)
double ty = height;
if (ySubsamplingFactor!=1)
ty /= ySubsamplingFactor;
double tx = width;
if (xSubsamplingFactor!=1)
tx /= xSubsamplingFactor;
transform.preConcatenate(AffineTransform.getTranslateInstance(tx,ty));
}
return transform;
}
@Override
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
initialize();
final int width = sasTile.getXPixels();
final int height = sasTile.getYPixels();
final int dataType = sasTile.getDataType();
final BandedSampleModel sampleModel = new BandedSampleModel(dataType, width, height, 1);
final ColorModel cm = ImageIOUtilities.createColorModel(sampleModel);
final List<ImageTypeSpecifier> l = new java.util.ArrayList<ImageTypeSpecifier>(1);
ImageTypeSpecifier imageType = new ImageTypeSpecifier(cm,sampleModel);
l.add(imageType);
return l.iterator();
}
}