Commit 357848ef authored by lafabregue's avatar lafabregue

switch to GDAL for image reading

parent 0fc1b77e
......@@ -17,3 +17,4 @@ local.properties
.settings/
.loadpath
*.hprof
/target/
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>JCL</groupId>
<artifactId>JCL</artifactId>
<version>0.0.1-SNAPSHOT</version>
<description>Java CLustering Library</description>
<build>
<sourceDirectory>src</sourceDirectory>
<resources>
<resource>
<directory>src</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<name>JCL</name>
<organization>
<name>SDC - iCube</name>
<url>http://icube-sdc.unistra.fr/fr/index.php/Accueil</url>
</organization>
<dependencies>
<dependency>
<groupId>ome</groupId>
<artifactId>formats-gpl</artifactId>
<version>5.7.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jfree</groupId>
<artifactId>jfreechart</artifactId>
<version>1.0.19</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>central</id>
<name>Central Repository</name>
<url>http://repo.maven.apache.org/maven2</url>
</repository>
<repository>
<id>unidata</id>
<name>Unidata Repository</name>
<url>http://artifacts.unidata.ucar.edu/content/repositories/unidata-releases</url>
</repository>
<repository>
<id>ome</id>
<name>OME Artifactory</name>
<url>http://artifacts.openmicroscopy.org/artifactory/maven/</url>
</repository>
</repositories>
</project>
\ No newline at end of file
package jcl.clustering.ImageBased;
import java.io.IOException;
import java.util.Vector;
import jcl.clustering.Cluster;
import jcl.clustering.ClusteringResult;
import jcl.clustering.LightCluster;
import jcl.data.DataObject;
import jcl.utils.Images.ImageReaderWrapper;
import jcl.utils.exceptions.MethodNotImplementedException;
import jcl.weights.ClassificationWeights;
import loci.formats.FormatException;
/**
* <p>
* Classe qui lit/écrit le resultat d'une classification d'une image via un fichier
* <p>
*
* @author baptistelafabregue
*
*/
public class ImagebasedClusteringResult extends ClusteringResult {
/** Chemin d'acces du fichier contenant le resultat de la classification */
private String filePath = "";
/** tableau qui contient pour chaque element d'un Data de type image son cluster de rattachement */
private byte[][] clusterMap = null;
/** non utilisé pour l'instant, mais doit pemerttre d'accéder au contenu de maniere streamee */
private int maxMemoryUsage = 0;
/** le format des pixel de l'image, correspond au formats de la classe FormatTool */
private int pixelType = 0;
/**
* <p>
* Constructeur
* <p>
*
* @param path
* le chemin d'acces du fichier contenant le resultat de la classification
* @param weights
* les ponderations utilisees
*/
public ImagebasedClusteringResult(String path, ClassificationWeights weights) {
this.filePath = path;
loadFromFile();
this.weights = weights;
this.randomColor();
}
/**
* <p>
* Constructeur
* <p>
*
* @param path
* le chemin d'acces du fichier contenant le resultat de la classification
* @param centroidsPath
* le chemin d'acces du fichier contenant la liste des centroids
* @param weights
* les ponderations utilisees
*/
public ImagebasedClusteringResult(String classifPath, String centroidsPath,
ClassificationWeights weights) {
this.filePath = classifPath;
loadFromFile();
Vector<DataObject> seeds = CentroidFileManager.readCentroidFile(centroidsPath);
this.clusters = new Cluster[seeds.size()];
for (int i = 0 ; i < seeds.size() ; i++) {
clusters[i] = new LightCluster(this, i, seeds.get(i));
}
this.weights = weights;
this.randomColor();
}
/**
* <p>
* Constructeur
* <p>
*
* @param path
* le chemin d'acces du fichier contenant le resultat de la classification
* @param seeds
* l'ensemble des centroides
* @param weights
* les ponderations utilisees
*/
public ImagebasedClusteringResult(String classifPath, Vector<DataObject> seeds,
ClassificationWeights weights) {
this.filePath = classifPath;
loadFromFile();
this.clusters = new Cluster[seeds.size()];
for (int i = 0 ; i < seeds.size() ; i++) {
clusters[i] = new LightCluster(this, i, seeds.get(i));
}
this.weights = weights;
this.randomColor();
}
/**
* <p>
* Constructeur de copie
* <p>
*
* @param result
* le FilebasedClusteringResult a copier
*/
public ImagebasedClusteringResult(ImagebasedClusteringResult result) {
this.filePath = result.getFilePath();
loadFromFile();
this.clusters = result.getClusters();
this.weights = (ClassificationWeights) result.weights.clone();
this.randomColor();
}
/** */
private static final long serialVersionUID = 1L;
@Override
public Object clone() {
return new ImagebasedClusteringResult(this);
}
@Override
public void deleteEmptyClusters() {
}
@Override
public int getClass(int o, boolean onWholeData) {
return clusterMap[o/clusterMap.length][o % clusterMap.length] & 0xFF;
}
@Override
public int[] getClusterMap() {
int[] result = new int[clusterMap.length*clusterMap[0].length];
int index = 0;
for (int j = 0 ; j <clusterMap[0].length ; j++) {
for (int i = 0 ; i < clusterMap.length ; i++) {
result[index] = clusterMap[i][j] & 0xFF;
index++;
}
}
return result;
}
@Override
public void setClusterMap(int[] clusterMap) {
if (clusterMap.length != this.clusterMap.length*this.clusterMap[0].length) {
new FormatException("Les nouvelles valeurs de cluster ne respectent pas les "
+ "dimensions de l'image en paramètre").printStackTrace();
}
int index = 0;
for (int j = 0 ; j < this.clusterMap[0].length ; j++) {
for (int i = 0 ; i < this.clusterMap.length ; i++) {
this.clusterMap[i][j] = (byte) clusterMap[index];
index++;
}
}
}
@Override
public int getNbObjects() {
if (clusterMap == null) {
return 0;
}
return clusterMap.length*clusterMap[0].length;
}
@Override
public void flushMemory() throws MethodNotImplementedException {
this.clusterMap = null;
}
/**
* <p>
* Renvoi le fichier qui contient le resultat de la classification
* <p>
*
* @return le chemin d'acces du fichier
*/
public String getFilePath() {
return filePath;
}
/**
* <p>
* Indique le fichier contenatn le resulat de la classification
* <p>
*
* @param filePath
* Le chemin d'acces du fichier
*/
public void setFilePath(String filePath) {
if (this.filePath != filePath) {
this.filePath = filePath;
loadFromFile();
}
}
private void loadFromFile() {
if (clusterMap == null) {
if (filePath != "") {
ImageReaderWrapper reader = new ImageReaderWrapper();
try {
reader.setImage(filePath);
pixelType = reader.getPixelType();
clusterMap = reader.get1BytePixels(0, 0, reader.getImageWidth(), reader.getImageHeight());
} catch (FormatException | IOException e) {
e.printStackTrace();
}
}
}
}
}
package jcl.data.ImageBased;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Vector;
import jcl.clustering.ClusteringResult;
import jcl.clustering.constraints.Constraint;
import jcl.data.Data;
import jcl.data.DataObject;
import jcl.data.SimpleData;
import jcl.data.attribute.Attribute;
import jcl.data.attribute.AttributeDCMDSequence;
import jcl.data.attribute.AttributeDSCMDSequence;
import jcl.data.attribute.AttributeHardConstrainedMultiDimSequence;
import jcl.data.attribute.AttributeHistogramSymbolique;
import jcl.data.attribute.AttributeMultiDimSequence;
import jcl.data.attribute.AttributeNumerical;
import jcl.data.attribute.AttributeSequence;
import jcl.data.attribute.AttributeSymbolicCategorial;
import jcl.data.mask.Mask;
import jcl.utils.Images.StreamedImageReaderWrapper;
import jcl.weights.Weights;
import jj2000.j2k.NotImplementedError;
/**
* <p>
* Specific implementation for Image files, that stream the data
* This implementation never holds the whole data in memory, so it can have some
* downsides in performance, but is efficient in memory consumption
* <p>
*
* @author Baptiste Lafabregue
*
*/
public class StreamedImageData extends Data {
/** */
private static final long serialVersionUID = 1L;
/** Object to read the image */
private Vector<StreamedImageReaderWrapper> reader = null;
/** Index of the current view */
//private int viewIndex = 0;
/** Size of the current view */
//private int viewSize = 0;
/** Total number of pixels in the image */
private int totalPixels = 0;
/**
* <p>
* Empty constructor
* </p>
*/
public StreamedImageData() {
}
/**
* <p>
* Constructor from a set of images file path.
* </p>
*
* @param filesNames
* Set of paths
*/
public StreamedImageData(final Vector<String> filesNames) {
this.dataFilesNames = filesNames;
for(String path : dataFilesNames) {
reader.add(new StreamedImageReaderWrapper(path));
totalPixels = reader.get(0).getImageHeight()*reader.get(0).getImageWidth();
}
}
/**
* <p>
* Copy constructor
* </p>
*
* @param data
* The data to copy
*/
public StreamedImageData(final StreamedImageData data) {
this.inertia = data.inertia;
this.comments = new String(data.comments);
this.dataName = new String(data.dataName);
this.model = data.model;
this.attributesNames = new String[data.attributesNames.length];
this.totalPixels = data.totalPixels;
this.dataFilesNames = (Vector<String>) data.getDataFilesName().clone();
reader = new Vector<StreamedImageReaderWrapper>();
for(String p : this.dataFilesNames) {
reader.add(new StreamedImageReaderWrapper(p));
}
if (data.locked != null) {
this.locked = data.locked.clone();
} else {
data.locked = null;
}
if (data.gravityCenter != null) {
this.gravityCenter = (DataObject) data.gravityCenter.clone();
} else {
this.gravityCenter = null;
}
for (int i = 0; i < data.attributesNames.length; i++) {
this.attributesNames[i] = new String(data.attributesNames[i]);
}
if (data.knownResult != null) {
this.knownResult = (ClusteringResult) data.knownResult.clone();
}
}
/**
*
* @param data
*/
public StreamedImageData(final StreamedImageData[] data) {
//TODO
}
/**
*
* @param data
* @param weights
*/
public StreamedImageData(final StreamedImageData data, final Weights weights) {
this.inertia = data.inertia;
this.comments = new String(data.comments);
this.dataName = new String(data.dataName);
this.model = data.model;
this.attributesNames = new String[data.attributesNames.length];
this.totalPixels = data.totalPixels;
this.dataFilesNames = (Vector<String>) data.getDataFilesName().clone();
reader = new Vector<StreamedImageReaderWrapper>();
for(String p : this.dataFilesNames) {
reader.add(new StreamedImageReaderWrapper(p));
}
if (data.locked != null) {
this.locked = data.locked.clone();
} else {
data.locked = null;
}
if (data.gravityCenter != null) {
this.gravityCenter = (DataObject) data.gravityCenter.clone();
} else {
this.gravityCenter = null;
}
for (int i = 0; i < data.attributesNames.length; i++) {
this.attributesNames[i] = new String(data.attributesNames[i]);
}
if (data.knownResult != null) {
this.knownResult = (ClusteringResult) data.knownResult.clone();
}
int nbAttributes = 0;
for (int i = 0; i < weights.getNbAttributes(); i++) {
if (weights.getWeight(i) > 0) {
nbAttributes++;
}
}
this.currentView = new ArrayList<DataObject>(data.currentView.size());
for (int i = 0; i < data.currentView.size(); i++) {
this.currentView.add(new DataObject(nbAttributes));
}
int current = 0;
for (int i = 0; i < weights.getNbAttributes(); i++) {
if (weights.getWeight(i) > 0) {
for (int j = 0; j < this.currentView.size(); j++) {
this.currentView.get(j).setAttribute(current, (Attribute) data.currentView.get(j).getAttribute(i).clone());
this.currentView.get(j).getAttribute(current).mul(weights.getWeight(i));
}
current++;
}
}
}
@Override
public Object clone() {
return new StreamedImageData(this);
}
@Override
public Iterator<DataObject> iterator() {
return new ImageDataIterator(this);
}
@Override
public Iterator<DataObject> iterator(int start, int end) {
return new ImageDataIterator(this, start, end);
}
@Override
public Data getSample(double ratio) {
return new SimpleData(this, ratio);
}
@Override
public Data[] kFolds(int k) {
System.err.println("Method not yet supported for images");
return null;
}
@Override
public Data leaveOneOut(int index) {
System.err.println("Method not yet supported for images");
return null;
}
@Override
public void setDataObject(int index, DataObject data) {
System.err.println("Method not yet supported for images");
}
@Override
public void setDataObject(int[] coordonneDataObject, DataObject data) {
System.err.println("Method not yet supported for images");
}
@Override
public Data[] split(double ratio) {
System.err.println("Method not yet supported for images");
return null;
}
@Override
public Data[] split(int[][] sets) {
System.err.println("Method not yet supported for images");
return null;
}
@Override
public DataObject mean() {
if (this.currentView == null) {
return null;
}
final int nbAttributes = this.currentView.get(0).getNbAttributes();
final DataObject res = new DataObject(nbAttributes);
Attribute sample;
for (int a = 0; a < nbAttributes; a++) {
sample = this.currentView.get(0).getAttribute(a);
if (sample instanceof AttributeNumerical) {
double value = 0.0;
for (int i = 0; i < this.currentView.size(); i++) {
value += this.currentView.get(i).getAttribute(a).getValue();
}
value = value / this.currentView.size();
res.setAttribute(a, new AttributeNumerical(value));
} else if (sample instanceof AttributeSequence) {
final AttributeSequence[] tab = new AttributeSequence[this.currentView.size()];
for (int i = 0; i < this.currentView.size(); i++) {
tab[i] = (AttributeSequence) this.currentView.get(i).getAttribute(a);
}
res.setAttribute(a, AttributeSequence.mean(tab));
} else if (sample instanceof AttributeMultiDimSequence) {
final AttributeMultiDimSequence[] tab = new AttributeMultiDimSequence[this.currentView.size()];
for (int i = 0; i < this.currentView.size(); i++) {
tab[i] = (AttributeMultiDimSequence) this.currentView.get(i).getAttribute(a);
}
res.setAttribute(a, AttributeMultiDimSequence.mean(tab));
} else if (sample instanceof AttributeHardConstrainedMultiDimSequence) {
final AttributeHardConstrainedMultiDimSequence[] tab = new AttributeHardConstrainedMultiDimSequence[this.currentView.size()];
for (int i = 0; i < this.currentView.size(); i++) {
tab[i] = (AttributeHardConstrainedMultiDimSequence) this.currentView.get(i).getAttribute(a);
}
res.setAttribute(a, AttributeHardConstrainedMultiDimSequence.mean(tab));
} else if (sample instanceof AttributeDSCMDSequence) {
final AttributeDSCMDSequence[] tab = new AttributeDSCMDSequence[this.currentView.size()];
for (int i = 0; i < this.currentView.size(); i++) {
tab[i] = (AttributeDSCMDSequence) this.currentView.get(i).getAttribute(a);
}
res.setAttribute(a, AttributeDSCMDSequence.mean(tab));
} else if (sample instanceof AttributeDCMDSequence) {
final AttributeDCMDSequence[] tab = new AttributeDCMDSequence[this.currentView.size()];
for (int i = 0; i < this.currentView.size(); i++) {
tab[i] = (AttributeDCMDSequence) this.currentView.get(i).getAttribute(a);
}
res.setAttribute(a, AttributeDCMDSequence.mean(tab));
} else if (sample instanceof AttributeHistogramSymbolique) {
final AttributeHistogramSymbolique[] tab = new AttributeHistogramSymbolique[this.currentView.size()];
for (int i = 0; i < this.currentView.size(); i++) {
tab[i] = (AttributeHistogramSymbolique) this.currentView.get(i).getAttribute(a);
}
res.setAttribute(a, AttributeHistogramSymbolique.mean(tab));
} else if (sample instanceof AttributeSymbolicCategorial) {
// TODO: réecrire en utilisant la nouvelle signature
return null;
} else {
return null;
}
}
return res;
}
@Override
public DataObject getDataObject(int i) {
System.err.println("Method not yet supported for images");
//TODO : a test should be done to test if the currentView has already been loaded
//TODO : it's not supposed to be the currentview index but the image index
return this.currentView.get(i);
}
@Override
public DataObject getDataObject(int[] coordonneDataObject) {
System.err.println("Method not yet supported for images");
//TODO : a test should be done to test if the currentView has already been loaded
//TODO : it's not supposed to be the currentview index but the image index
int aux = 1;
int index = coordonneDataObject[coordonneDataObject.length - 1];
for (int i = coordonneDataObject.length - 2; i > -1; i--) {
aux *= this.dimensions[i + 1];
index += coordonneDataObject[i] * aux;
}
return this.currentView.get(index);
}
@Override
public DataObject getDataObject(int x, int y) {
//TODO : a test should be done to test if the currentView has already been loaded
final int index = x * this.dimensions[1] + y;
return this.currentView.get(index);
}
@Override
public int getNbObjects() {
return totalPixels;
}
@Override
public DataObject getOneDataObject() {
//TODO : a test should be done to test if the currentView has already been loaded
return this.currentView.get(0);
}
@Override
public int getWholeDataNbObjects() {
return totalPixels;
}
private static final class ImageDataIterator implements Iterator<DataObject> {
private int cursor;
private final int end;
private StreamedImageData data;
public ImageDataIterator(final StreamedImageData data) {
this.data = data;
this.cursor = 0;
this.end = data.currentView.size();
}
public ImageDataIterator(final StreamedImageData data, int start, int end) {
this.data = data;
this.cursor = start;
this.end = end;
}
public boolean hasNext() {
return this.cursor < end;
}
public DataObject next() {
if(this.hasNext()) {
DataObject current = null;
cursor++;
// voir Image Sampler pour un exemple
throw new NotImplementedError("TODO : implement next() function in StreamedImageData");
//return current;
}
throw new NoSuchElementException();
}