Commit a4f994f2 authored by lafabregue's avatar lafabregue

switch to GDAL for image reading

parent 82293dbd
......@@ -238,10 +238,12 @@ public class ImageSession extends ImageManager {
public void mask() {
JFileChooser fileChooser = new JFileChooser(Messages.getString("ImageSession.9")); //$NON-NLS-1$
int returnVal = fileChooser.showOpenDialog(((ImagePanel) this.displayer));
fileChooser.setCurrentDirectory(MainFrame.getInstance().getCurrentDirectory());
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fileChooser.getSelectedFile();
this.rawImage.setMask(file);
MainFrame.getInstance().setCurrentDirectory(file.getParentFile());
}
if (displayEnable) {
......
......@@ -150,6 +150,13 @@ public class MainFrame extends JFrame {
/** The session storing tree for datas */
private DefaultTreeModel mDataTreeModel;
/** saved dimensions used to display side menu panels */
private Dimension mImageProjectDimension = new Dimension(180, 100);
private Dimension mDataProjectDimension = new Dimension(160, 50);
private Dimension mInfoDimension = new Dimension(180, 100);
/**
* Nombre de threads
......@@ -1464,13 +1471,29 @@ public class MainFrame extends JFrame {
treeDataPanel.add(treeDataScrollPane, BorderLayout.CENTER);
sif_treeDataPanel = new SimpleInternalFrame(Messages.getString("MainFrame.69")); //$NON-NLS-1$
sif_treeDataPanel.setPreferredSize(new Dimension(160, 50));
sif_treeDataPanel.setPreferredSize(mDataProjectDimension);
sif_treeDataPanel.add(treeDataPanel);
// Creation of the Project Tree Interface for images
JPanel treeImagePanel = new JPanel(new BorderLayout());
JScrollPane treeImageScrollPane = new JScrollPane(this.mImageProjectTree);
treeImageScrollPane.setPreferredSize(new Dimension(180, 100));
treeImageScrollPane.setPreferredSize(mImageProjectDimension);
treeDataScrollPane.addComponentListener(new ComponentListener() {
@Override
public void componentShown(ComponentEvent e) {
}
@Override
public void componentResized(ComponentEvent e) {
mImageProjectDimension = e.getComponent().getSize();
}
@Override
public void componentMoved(ComponentEvent e) {
}
@Override
public void componentHidden(ComponentEvent e) {
}
});
treeImagePanel.add(treeImageScrollPane, BorderLayout.CENTER);
sif_treeImagePanel = new SimpleInternalFrame(Messages.getString("MainFrame.691")); //$NON-NLS-1$
......
......@@ -18,10 +18,11 @@ import javax.swing.JTextField;
import jcl.data.mask.IntArrayMask;
import jcl.data.mask.Mask;
import jcl.data.mask.MultiIDIntArrayMask;
import jcl.utils.Images.StreamedImageReaderWrapper;
import mustic.gui.dialog.Messages;
import mustic.gui.panels.data.DataConstructPanel;
import mustic.io.RawImage;
import mustic.utils.image.reader.GDALImageReaderWrapper;
import mustic.utils.image.reader.ReaderWrapper;
import mustic.utils.io.CSVUtils;
......@@ -168,7 +169,7 @@ public class MaskImportDialog extends JDialog {
// get the mask itself
int[] content = null;
try {
StreamedImageReaderWrapper reader = new StreamedImageReaderWrapper(path);
ReaderWrapper reader = new GDALImageReaderWrapper(path);
content = new int[reader.getImageWidth()*reader.getImageHeight()];
int index = 0;
for(int y = 0 ; y < reader.getImageHeight() ; y++) {
......
......@@ -23,7 +23,6 @@ import jcl.data.Data;
import jcl.data.DataObject;
import jcl.data.SimpleData;
import jcl.data.attribute.AttributeMultiDimSequence;
import jcl.data.sampling.ImportedImageSampler;
import mustic.gui.MainFrame;
import mustic.gui.dialog.Messages;
import mustic.gui.dialog.SequenceDialog;
......@@ -31,6 +30,7 @@ import mustic.io.ImageData;
import mustic.io.RawImage;
import mustic.utils.documentFilter.ToUpdateObject;
import mustic.utils.io.dataExchange.DataArffExchange;
import mustic.utils.jclAdapters.ImportedImageSampler;
/**
......
package mustic.gui.dialog.csv;
import java.awt.BorderLayout;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
......@@ -12,7 +11,6 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import javax.swing.JButton;
import javax.swing.JFileChooser;
......@@ -22,24 +20,15 @@ import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import org.hibernate.mapping.Array;
import jcl.data.Data;
import jcl.data.DataObject;
import jcl.data.SimpleData;
import jcl.data.attribute.AttributeMultiDimSequence;
import jcl.data.sampling.ImportedImageSampler;
import mustic.gui.MainFrame;
import mustic.gui.dialog.Messages;
import mustic.gui.dialog.SequenceDialog;
import mustic.io.ImageData;
import mustic.io.RawImage;
import mustic.utils.documentFilter.ToUpdateObject;
import mustic.utils.image.ImageHelper;
import mustic.utils.io.CSVUtils;
import mustic.utils.io.dataExchange.DataArffExchange;
import weka.core.Instance;
import weka.core.Instances;
/**
......@@ -151,7 +140,7 @@ public class CSVToDataDialog extends JInternalFrame implements ToUpdateObject {
}
}
@SuppressWarnings({ "unchecked", "deprecation" })
@SuppressWarnings({"deprecation"})
protected void buttonOk_actionPerformed() {
try {
List<DataObject> result = new ArrayList<DataObject>();
......
......@@ -16,7 +16,6 @@ import com.l2fprod.common.swing.JTaskPane;
import com.l2fprod.common.swing.JTaskPaneGroup;
import jcl.data.mask.Mask;
import jcl.data.sampling.ImageSampler;
import jcl.data.sampling.Sampler;
import mustic.gui.ImageDesktopFrame;
import mustic.gui.ImageSession;
......@@ -27,6 +26,7 @@ import mustic.gui.panels.data.components.SourceSelectionPanel;
import mustic.io.ImageData;
import mustic.io.RawImage;
import mustic.utils.documentFilter.ToUpdateObject;
import mustic.utils.jclAdapters.ImageSampler;
/**
* Display the tools to generate a Data object
......
......@@ -19,8 +19,6 @@ import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import jcl.data.sampling.ImageSampler;
import loci.formats.FormatException;
import mustic.gui.ImageDesktopFrame;
import mustic.gui.ImageSession;
import mustic.gui.MainFrame;
......@@ -29,6 +27,7 @@ import mustic.gui.panels.data.DataConstructPanel;
import mustic.gui.panels.data.Messages;
import mustic.io.MusticImageMask;
import mustic.io.RawImage;
import mustic.utils.jclAdapters.ImageSampler;
/**
* JPanel that allows the user to select the source for a data
......@@ -333,7 +332,7 @@ public class SourceSelectionPanel extends JPanel {
} else {
fatherContainer.setSample(new ImageSampler(filesPaths));
}
} catch (FormatException e) {
} catch (Exception e) {
e.printStackTrace();
}
}
......@@ -345,8 +344,12 @@ public class SourceSelectionPanel extends JPanel {
int index = monoDateComboBox.getSelectedIndex();
if (index < desktopFrames.length && index != -1) {
fatherContainer.setSample(new ImageSampler(allImageDesktopFrames[index]
.getImageSession().getImageFilePath()));
try {
fatherContainer.setSample(new ImageSampler(allImageDesktopFrames[index]
.getImageSession().getImageFilePath()));
} catch (Exception e) {
e.printStackTrace();
}
Vector<RawImage> filesPath = new Vector<RawImage>();
filesPath.add(allImageDesktopFrames[index]
.getImageSession().getRawImage());
......
......@@ -9,12 +9,11 @@ import jcl.data.SimpleData;
import jcl.data.attribute.AttributeMultiDimSequence;
import jcl.data.attribute.AttributeNumerical;
import jcl.data.mask.Mask;
import jcl.data.sampling.ImageSampler;
import jcl.data.sampling.Sampler;
import jcl.utils.exceptions.MethodNotImplementedException;
import mustic.gui.ImageSession;
import mustic.gui.MainFrame;
import mustic.models.thread.ProgressThreadJCL;
import mustic.utils.jclAdapters.ImageSampler;
/**
* <p>
......
......@@ -8,8 +8,8 @@ import java.io.IOException;
import java.util.Iterator;
import jcl.data.mask.Mask;
import jcl.utils.Images.StreamedImageReaderWrapper;
import loci.formats.FormatException;
import mustic.utils.image.reader.GDALImageReaderWrapper;
import mustic.utils.image.reader.ReaderWrapper;
//import mustic.io.bsq.BSQImage;
......@@ -122,18 +122,18 @@ public class MusticImageMask implements Mask {
public final static String EXTENSION = ".mask";
public MusticImageMask(File file) {
StreamedImageReaderWrapper reader = new StreamedImageReaderWrapper(file.getAbsolutePath());
width = reader.getImageWidth();
height = reader.getImageHeight();
res = new boolean[width][height];
try {
ReaderWrapper reader = new GDALImageReaderWrapper(file.getAbsolutePath());
width = reader.getImageWidth();
height = reader.getImageHeight();
res = new boolean[width][height];
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
res[x][y] = (reader.getPixel(x, y)[0] == 0) ? true : false;
}
}
} catch (FormatException | IOException e) {
} catch (Exception e) {
e.printStackTrace();
}
}
......
......@@ -18,6 +18,8 @@ import mustic.gui.Messages;
import mustic.gui.dialog.MaskImportDialog;
import mustic.io.roi.ROIImage;
import mustic.io.roi.RegionOfInterest;
import mustic.utils.image.reader.GDALImageReaderWrapper;
import mustic.utils.image.reader.ReaderWrapper;
import otbAppWrapping.*;
import com.l2fprod.common.propertysheet.DefaultProperty;
......@@ -25,7 +27,6 @@ import com.l2fprod.common.propertysheet.Property;
import jcl.data.mask.Mask;
import jcl.utils.MemoryFlush;
import jcl.utils.Images.StreamedImageReaderWrapper;
import jcl.utils.exceptions.MethodNotImplementedException;
/**
......@@ -2253,20 +2254,20 @@ public class RawImage extends Observable implements Serializable, MemoryFlush
* Compute teh min/max values per channel
*/
private void computeMinMax() {
StreamedImageReaderWrapper reader = new StreamedImageReaderWrapper(filename);
long allocatedMemory = (Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory());
long presumableFreeMemory = Runtime.getRuntime().maxMemory() - allocatedMemory;
// we just tack the 2/3rd of the memory available;
presumableFreeMemory *= 0.05;
try {
ReaderWrapper reader = new GDALImageReaderWrapper(filename);
reader.setMaxRAM(presumableFreeMemory);
} catch (IOException e) {
minValues = reader.getMinValues();
maxValues = reader.getMaxValues();
scaledMinValues = reader.getMinValues();
scaledMaxValues = reader.getMaxValues();
} catch (Exception e) {
e.printStackTrace();
}
minValues = reader.getMinValues();
maxValues = reader.getMaxValues();
scaledMinValues = reader.getMinValues();
scaledMaxValues = reader.getMaxValues();
}
@Override
......
......@@ -9,10 +9,8 @@ import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.awt.image.SinglePixelPackedSampleModel;
import java.awt.image.WritableRaster;
import java.io.IOException;
import jcl.utils.Images.StreamedImageReaderWrapper;
import loci.formats.FormatException;
import mustic.utils.image.reader.ReaderWrapper;
/**
* Class that allows buffer manipulation and creation from images.
......@@ -213,12 +211,12 @@ public class BufferedImageHelper {
* a reader that points to the image to use
*
* @return the BufferedImage containing the selection
* @throws IOException If there is a problem reading the file
* @throws FormatException If there is a problem parsing the image's metadata
* @throws Exception If there is a problem reading the file
* If there is a problem parsing the image's metadata
*/
public static BufferedImage getSubImage(int imageX, int imageY, int imageWidth, int imageHeight,
int r, int g, int b, double[] minValues, double[] maxValues,
StreamedImageReaderWrapper reader) throws FormatException, IOException {
ReaderWrapper reader) throws Exception {
if (r < 0 || r >= reader.getChannelCount() || g < 0 || g >= reader.getChannelCount()
|| b < 0 || b >= reader.getChannelCount()) {
throw new IndexOutOfBoundsException("R, G and B index should respect"
......
package mustic.utils.image.reader;
import org.gdal.gdal.Band;
import org.gdal.gdal.Dataset;
import org.gdal.gdal.gdal;
import org.gdal.gdalconst.gdalconst;
import jcl.utils.exceptions.MethodNotImplementedException;
/**
* Reader tool used to access to the data using the gdal library
* @author Baptiste LAFABREGUE
*
*/
public class GDALImageReaderWrapper extends ReaderWrapper {
/** the dataset that link to the image to read */
private Dataset dataSet = null;
/** the projection of the image */
private String projection = "";
/** the geotransform of the image */
private double[] geoTransform = null;
private boolean approxOK = true;
private double[][] tile = null;
private String filePath = null;
/** */
private static final long serialVersionUID = 1L;
/**
* <p>
* Constructor
* <p>
*
* @param path
* The image file path
* @throws Exception
* Error when creating the GDAL DataSet
*/
public GDALImageReaderWrapper(String Path) throws Exception {
this.filePath = Path;
gdal.AllRegister();
dataSet = gdal.Open(Path, gdalconst.GA_ReadOnly);
if (dataSet == null) {
throw new Exception("GDAL : Error when opening the file");
} else {
this.tile = null;
this.imageWidth = dataSet.GetRasterXSize();
this.imageHeight = dataSet.GetRasterYSize();
this.channelCount = dataSet.GetRasterCount();
// we set the optimal tile width to the first band of the raster
this.optimaleTileWidth = dataSet.GetRasterBand(1).GetBlockXSize();
this.optimaleTileHeight = dataSet.GetRasterBand(1).GetBlockYSize();
this.tileWidth = this.optimaleTileWidth;
this.tileHeight = this.optimaleTileHeight;
this.endY = imageHeight;
this.bytesPerPixel = 64;
this.projection = dataSet.GetProjectionRef();
this.geoTransform = dataSet.GetGeoTransform();
}
}
public GDALImageReaderWrapper(GDALImageReaderWrapper gdalImageReaderWrapper) throws Exception {
this.filePath = gdalImageReaderWrapper.filePath;
gdal.AllRegister();
dataSet = gdal.Open(gdalImageReaderWrapper.filePath, gdalconst.GA_ReadOnly);
if (dataSet == null) {
throw new Exception("GDAL : Error when opening the file");
} else {
this.tile = null;
this.imageWidth = dataSet.GetRasterXSize();
this.imageHeight = dataSet.GetRasterYSize();
this.channelCount = dataSet.GetRasterCount();
// we set the optimal tile width to the first band of the raster
this.optimaleTileWidth = dataSet.GetRasterBand(1).GetBlockXSize();
this.optimaleTileHeight = dataSet.GetRasterBand(1).GetBlockYSize();
this.tileWidth = this.optimaleTileWidth;
this.tileHeight = this.optimaleTileHeight;
this.endY = imageHeight;
this.bytesPerPixel = 64;
this.projection = dataSet.GetProjectionRef();
this.geoTransform = dataSet.GetGeoTransform();
this.extrema = gdalImageReaderWrapper.extrema;
setMaxRAM(gdalImageReaderWrapper.maxRAM);
}
}
@Override
protected double[][] getExtrema() {
if (extrema == null) {
extrema = new double[2][channelCount];
for (int i = 0 ; i < channelCount ; i++) {
Double[] min = new Double[1];
Double[] max = new Double[1];
dataSet.GetRasterBand(i+1).GetMinimum(min);
dataSet.GetRasterBand(i+1).GetMaximum(max);
if (min[0] == null || max[0] == null) {
double[] minMax = new double[2];
int approx = 1;
if (!approxOK) {
approx = 0;
}
dataSet.GetRasterBand(i+1).ComputeRasterMinMax(minMax, approx);
extrema[0][i] = minMax[0];
extrema[1][i] = minMax[1];
} else {
extrema[0][i] = min[0];
extrema[1][i] = max[0];
}
}
}
return extrema;
}
@Override
public double[] getPixel(int x, int y) throws Exception {
double[] result = new double[channelCount];
if(!isInImage(x,y)) {
return result;
}
if (!isInTile(x, y)) {
loadTile(x, y);
}
int index = x - tileX + (y - tileY) * currentTileWidth;
for (int i = 0 ; i < channelCount ; i++) {
result[i] = tile[i][index];
}
return result;
}
@Override
public void loadTile(int x, int y) throws Exception {
tileX = x - x % tileWidth;
tileY = y - y % tileHeight;
if (tileY < startY) {
tileY = startY;
}
if (tileX < startX) {
tileX = startX;
}
currentTileHeight = tileHeight;
if (endY > 0) {
if (currentTileHeight+tileY > endY)
currentTileHeight = endY - tileY;
} else {
if (currentTileHeight+tileY > imageHeight)
currentTileHeight = imageHeight - tileY;
}
currentTileWidth = tileWidth;
if (endX > -1) {
if (currentTileWidth+tileX > endX)
currentTileWidth = endX - tileX;
} else {
if (currentTileWidth+tileX > imageWidth)
currentTileWidth = imageWidth - tileX;
}
tile = new double[channelCount][currentTileWidth*currentTileHeight];
for (int i = 0 ; i < channelCount ; i++) {
Band band = dataSet.GetRasterBand(i+1);
int flag = band.ReadRaster(tileX, tileY, currentTileWidth, currentTileHeight, tile[i]);
if (flag != gdalconst.CE_None) {
throw new Exception("GDAL : Error while reading the raster band of image "+dataSet.GetDescription());
}
}
tileLoaded = true;
// System.out.println("Tile of "+currentTileWidth+"x"+currentTileHeight+" - "+tileX+","+tileY+" for image of "+imageWidth+"x"+imageHeight);
}
@Override
public int getBytesPerPixel() {
//TODO : not implemented yet
return 0;
}
@Override
public Object clone() {
try {
return new GDALImageReaderWrapper(this);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* Fetch the affine transformation coefficients.
* Fetches the coefficients for transforming between pixel/line (P,L) raster space, and projection coordinates (Xp,Yp) space.
*
* Xp = geoTransformArray[0] + P*geoTransformArray[1] + L*geoTransformArray[2];
* Yp = geoTransformArray[3] + P*geoTransformArray[4] + L*geoTransformArray[5];
*
* In a north up image, geoTransformArray[1] is the pixel width, and geoTransformArray[5] is the pixel height. The upper left corner of the upper left pixel is at position (geoTransformArray[0],geoTransformArray[3]).
* The default transform is (0,1,0,0,0,1) and should be returned even when an error occurs, such as for formats that don't support transformation to projection coordinates.
*
* @return the transformation coefficients
*/
public double[] getGeoTransform() {
return geoTransform;
}
public String getProjection() {
return projection;
}
@Override
public void flushMemory() throws MethodNotImplementedException {
tile = null;
dataSet.delete();
}
public static void main(String[] args) {
String path = "/home/baptiste/Data/malaoui/evi_2013_2014.tif";
long allocatedMemory = (Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory());
long presumableFreeMemory = Runtime.getRuntime().maxMemory() - allocatedMemory;
// we just tack the 2/3rd of the memory available;
presumableFreeMemory *= 0.05;
long memoryPerReader = presumableFreeMemory ;
try {
GDALImageReaderWrapper reader = new GDALImageReaderWrapper(path);
reader.setMaxRAM(memoryPerReader);
double[] pixel = reader.getPixel(0, 0);
System.out.println("pixel 0,0" );
for (int i = 0 ; i < pixel.length ; i++) {
System.out.print(pixel[i]+";");
}
System.out.print("\n");
pixel = reader.getPixel(2000, 2000);
System.out.println("pixel 2000,2000" );
for (int i = 0 ; i < pixel.length ; i++) {
System.out.print(pixel[i]+";");
}
System.out.print("\n");
pixel = reader.getPixel(2051, 6000);
System.out.println("pixel 2000,2000" );
for (int i = 0 ; i < pixel.length ; i++) {
System.out.print(pixel[i]+";");
}
System.out.print("\n");
pixel = reader.getPixel(2052, 6000);
System.out.println("pixel 2000,2000" );
for (int i = 0 ; i < pixel.length ; i++) {
System.out.print(pixel[i]+";");
}
System.out.print("\n");
} catch (Exception e) {
e.printStackTrace();
}
// long startT = System.nanoTime();
// System.out.println("start at "+startT);
// double[][] extrema = reader.getExtrema();
// long endT = System.nanoTime();
// System.out.println("endat "+ endT + " total of "+ (endT - startT)/1000000l +"ms");
// System.out.println("extrema : "+Arrays.deepToString(extrema));
}
}
package mustic.utils.image.reader;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.Arrays;
import loci.formats.FormatException;
import loci.formats.FormatTools;
import loci.formats.ImageReader;
/**
* <p>
* Wrapper au dessus des fonctions de lecture d'image de la libraire
* bio-formats qui permet de lire les images par tuile.
* <p>
*
* @author Baptiste LAFABREGUE
*
*/
public class ImageReaderWrapper {
ImageReader reader = null;
int currentImageIndex = 0;
public ImageReaderWrapper() {
reader = new ImageReader();
}
/**
* <p>
* Definit l'image a utiliser
* <p>
*
* @param path
* le chemin d'acces de l'image
*
* @throws FormatException
* @throws IOException
*/
public void setImage(String path) throws FormatException, IOException {
reader.setId(path);
}
/**
* <p>
* Renvoi un tableau de byte qui contient pour chaque coordonnee contient sa
* valeur sous la forme d'un tableau de byte pour une vue donnee de l'image.
* Format : byte[x][y][pixels]
*
* A noter que si les pixels font n bytes, les n premiers bytes seront pour la bande 1,
* les n suivant pour la bande 2, ect ..
* <p>
*
* @param x
* l'absisse du coin haut-gauche de la vue
* @param y
* l'ordonnee du coin haut-gauche de la vue
* @param w
* la largeur de la vue
* @param h
* la hauteur de la vue
* @return le tableau de byte contenant les valeus de la vue
*
* @throws IOException
* si il y a eu un probleme en lisant le fichier
* @throws FormatException
* si il y a eu un probleme en parsant les metadata du fichier
*/
public byte[][][] getPixels(int x, int y, int w, int h) throws FormatException, IOException {
byte[] rawData = reader.openBytes(currentImageIndex, x, y, w, h);
int channelCount = reader.getRGBChannelCount();
int pixelSize = reader.getBitsPerPixel()/8;
// test si on a bien le nombre attendu de pixels
if(rawData.length != w * h * channelCount * pixelSize) {
throw new IndexOutOfBoundsException("La taille du tableau de byte renvoye n'est pas celle attendue.");
}
int channelGap = rawData.length / channelCount;
byte[][][] result = new byte[w][h][channelCount*pixelSize];
int index = 0;
for(int j = 0 ; j < h ; j++) {
for(int i = 0 ; i < w ; i++) {
for(int c = 0 ; c < channelCount ; c++) {
for(int p = 0 ; p < pixelSize ; p++) {
result[i][j][c*pixelSize+p] = rawData[index+channelGap*c+p];
}
}
index++;
}
}
return result;
}
/**