/*
* 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;
import it.geosolutions.imageio.stream.input.FileImageInputStreamExt;
import it.geosolutions.imageio.stream.input.FileImageInputStreamExtImpl;
import it.geosolutions.imageio.utilities.Utilities;
import java.awt.geom.AffineTransform;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.stream.ImageInputStream;
import com.jmatio.io.MatFileFilter;
import com.jmatio.io.MatFileReader;
import com.jmatio.types.MLArray;
import com.jmatio.types.MLChar;
/**
* Main abstract class defining a reader to access Matlab 5 files.
*
* @author Daniele Romagnoli, GeoSolutions.
* @author Simone Giannecchini, GeoSolutions.
*/
public abstract class MatFileImageReader extends ImageReader {
/** The LOGGER for this class. */
private static final Logger LOGGER = Logger
.getLogger("it.geosolutions.imageio.matfile5");
public void setInput(Object input, boolean seekForwardOnly) {
this.setInput(input, seekForwardOnly, false);
}
/** The ImageInputStream */
private ImageInputStream imageInputStream;
/** The input dataSource */
private File dataSource = null;
protected MatFileReader matReader = null;
/** Contains the name of the underlying data arrays
* The implementation uses a LinkedList in order to associate
* imageIndexes to arrays name
*/
protected List<String> dataArrays = new LinkedList<String>();
/**
* Constructs a <code>MatFileImageReader</code> using a
* {@link MatFileImageReaderSpi}.
*
* @param originatingProvider
* The {@link MatFileImageReaderSpi} to use for building this
* <code>MatFileImageReader</code>.
*/
protected MatFileImageReader(MatFileImageReaderSpi originatingProvider) {
super(originatingProvider);
}
protected abstract void initialize();
/**
* Tries to retrieve the data Source for the ImageReader's input.
*/
protected File getDatasetSource(Object myInput) {
if (dataSource == null) {
if (myInput instanceof File)
dataSource = (File) myInput;
else if (myInput instanceof FileImageInputStreamExt)
dataSource = ((FileImageInputStreamExt) myInput).getFile();
else if (input instanceof URL) {
final URL tempURL = (URL) input;
if (tempURL.getProtocol().equalsIgnoreCase("file")) {
dataSource = Utilities.urlToFile(tempURL);
}
} else
// should never happen
throw new RuntimeException(
"Unable to retrieve the Data Source for"
+ " the provided input");
}
return dataSource;
}
/**
* Sets the input for the specialized reader.
*
* @throws IllegalArgumentException
* if the provided input is <code>null</code>
*/
public void setInput(Object input, boolean seekForwardOnly,
boolean ignoreMetadata) {
if (LOGGER.isLoggable(Level.FINE))
LOGGER.fine("Setting Input");
// Prior to set a new input, I need to do a pre-emptive reset in order
// to clear any value-object which was related to the previous input.
if (this.imageInputStream != null) {
reset();
imageInputStream = null;
}
if (input == null)
throw new IllegalArgumentException("The provided input is null!");
// //
//
// File input
//
// //
if (input instanceof File) {
dataSource = (File) input;
try {
imageInputStream = new FileImageInputStreamExtImpl((File)input);
} catch (IOException e) {
throw new RuntimeException(
"Failed to create a valid input stream ", e);
}
}
// //
//
// FileImageInputStreamExt input
//
// //
else if (input instanceof FileImageInputStreamExt) {
dataSource = ((FileImageInputStreamExt) input).getFile();
imageInputStream = (ImageInputStream) input;
}
// //
//
// URL input
//
// //
else if (input instanceof URL) {
final URL tempURL = (URL) input;
if (tempURL.getProtocol().equalsIgnoreCase("file")) {
try {
dataSource = Utilities.urlToFile(tempURL);
imageInputStream = ImageIO.createImageInputStream(input);
} catch (IOException e) {
throw new RuntimeException(
"Failed to create a valid input stream ", e);
}
}
}
// /////////////////////////////////////////////////////////////////////
//
// Checking if this input is of a supported format.
// Now, I have an ImageInputStream and I can try to see if the input's
// format is supported by the specialized reader
//
// /////////////////////////////////////////////////////////////////////
boolean isInputDecodable = false;
try {
isInputDecodable = ((MatFileImageReaderSpi) this
.getOriginatingProvider()).isDecodable(dataSource
.getAbsolutePath());
} catch (IOException e) {
throw new RuntimeException(
"The provided input is not supported by this reader", e);
}
if (isInputDecodable)
super.setInput(imageInputStream, seekForwardOnly, ignoreMetadata);
else {
StringBuilder sb = new StringBuilder();
if (imageInputStream == null)
sb.append("Unable to create a valid ImageInputStream for the provided input:").append("\n")
.append(input.toString());
else
sb.append("The provided input is not supported by this reader");
throw new RuntimeException(sb.toString());
}
}
/**
* Allows resources to be released
*/
public synchronized void dispose() {
super.dispose();
if (imageInputStream != null)
try {
imageInputStream.close();
} catch (IOException ioe) {
}
imageInputStream = null;
if (matReader != null)
matReader.dispose();
if (dataArrays != null){
dataArrays.clear();
dataArrays = null;
}
matReader = null;
}
/**
* Reset main values
*/
public synchronized void reset() {
super.setInput(null, false, false);
dispose();
}
public int getNumImages(boolean allowSearch) throws IOException {
if (LOGGER.isLoggable(Level.FINE))
LOGGER.fine("getting NumImages");
return 1;
}
@Override
public IIOMetadata getStreamMetadata() throws IOException {
// TODO Auto-generated method stub
return null;
}
protected static void initFilter(MatFileFilter filter, Set<String> filterElements) {
if (filterElements != null && !filterElements.isEmpty()) {
for (String element : filterElements) {
filter.addArrayName(element);
}
}
}
/**
* Get an AffineTransform to filter the matrix
* @param param an ImageReadParam used to compute scales and translates by checking the
* subsampling as well as the source region.
*
* @return
* @throws IOException
*/
protected AffineTransform getAffineTransform(final ImageReadParam param) throws IOException{
//Compute standard transpose
AffineTransform transposed = getTransposed(param);
//preconcatenate additional transformation, depending on the data interpretation
transposed.preConcatenate(getPreTransform(param));
return transposed;
}
/**
* The Standard implementation returns a simple Identity.
* Special Implementations may add additional transformation to be preconcatenated
* @param param
* @return
* @throws IOException
*/
protected AffineTransform getPreTransform(final ImageReadParam param) throws IOException {
return AffineTransform.getRotateInstance(0.0);//identity
}
/**
* Note that the underlying matrix fills 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
*
* 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
*
*
* @param param
* @return
*/
private AffineTransform getTransposed(ImageReadParam param) {
final AffineTransform transform = AffineTransform.getRotateInstance(0);// identity
if (param!=null){
final int xSubsamplingFactor = param.getSourceXSubsampling();
final int ySubsamplingFactor = param.getSourceYSubsampling();
if (xSubsamplingFactor != 1 || ySubsamplingFactor != 1) {
transform.preConcatenate(AffineTransform.getScaleInstance(1.0d/ySubsamplingFactor, 1.0d/xSubsamplingFactor));
}
}
// //
//
// Transposing the Matlab data matrix
//
// //
AffineTransform transposeTransform = AffineTransform.getRotateInstance(0);
transposeTransform.preConcatenate(AffineTransform.getScaleInstance(1,-1));
transposeTransform.preConcatenate(AffineTransform.getRotateInstance(Math.PI*0.5d));
transform.preConcatenate(transposeTransform);
return transform;
}
}