/*
* Copyright (C) 2010-2014 Mathias Unberath
* CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
*/
package edu.stanford.rsl.conrad.io;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.StringTokenizer;
import edu.stanford.rsl.conrad.geometry.shapes.activeshapemodels.PCA;
import edu.stanford.rsl.conrad.numerics.SimpleMatrix;
import edu.stanford.rsl.conrad.numerics.SimpleVector;
/**
* Class to read and write Eigenvectors, Eigenvalues and the consensus as produced by a principal component analysis.
* @author Mathias Unberath
*
*/
public class PcaIO {
/**
* Dimension of vertices in a mesh object. Default is 1;
*/
private int pointDimension = 1;
/**
* The filename for input or output operation.
*/
String filename;
/**
* Array containing the eigenvalues of the covariance matrix after singular value decomposition.
*/
private double[] eigenValues;
/**
* Matrix containing the Eigenvectors of the covariance matrix after singular value decomposition.
*/
private SimpleMatrix eigenVectors;
/**
* Connectivity information when dealing with meshes.
*/
private SimpleMatrix connectivity;
/**
* The mean shape.
*/
private SimpleVector consensus;
//==========================================================================================
// METHODS
//==========================================================================================
/**
* Default constructor.
*/
public PcaIO(){
}
/**
* Constructs the object and sets the filename for reading operation.
* @param filename The file to be read.
*/
public PcaIO(String filename){
this.filename = filename;
}
/**
* Reads the variances from the file in the filename class member and stores the data in the corresponding class members.
* @throws IOException
*/
public void readVarianceOnly() throws IOException{
assert(filename != null) : new Exception("Filename not set.");
FileReader fr = new FileReader(filename);
BufferedReader br = new BufferedReader(fr);
String line = br.readLine();
StringTokenizer tok = new StringTokenizer(line);
tok.nextToken(); // skip "DIMENSION"
String t = tok.nextToken();
@SuppressWarnings("unused")
int rows = Integer.parseInt(t);
t = tok.nextToken();
int cols = Integer.parseInt(t);
// read point dimension
line = br.readLine();
tok = new StringTokenizer(line);
tok.nextToken(); // skip "POINTDIMENSION"
this.pointDimension = Integer.parseInt(tok.nextToken());
// allocate class members
this.eigenValues = new double[cols];
br.readLine(); // skip "EIGENVALUES"
line = br.readLine(); // read eigenvalues
tok = new StringTokenizer(line);
for(int i = 0; i < cols; i++){
eigenValues[i] = Double.parseDouble(tok.nextToken());
}
br.close();
fr.close();
}
/**
* Reads the file in the filename class member and stores the data in the corresponding class members.
* @throws IOException
*/
public void readFile() throws IOException{
assert(filename != null) : new Exception("Filename not set.");
FileReader fr = new FileReader(filename);
BufferedReader br = new BufferedReader(fr);
String line = br.readLine();
StringTokenizer tok = new StringTokenizer(line);
tok.nextToken(); // skip "DIMENSION"
String t = tok.nextToken();
int rows = Integer.parseInt(t);
t = tok.nextToken();
int cols = Integer.parseInt(t);
// read point dimension
line = br.readLine();
tok = new StringTokenizer(line);
tok.nextToken(); // skip "POINTDIMENSION"
this.pointDimension = Integer.parseInt(tok.nextToken());
// allocate class members
this.eigenValues = new double[cols];
this.eigenVectors = new SimpleMatrix(rows, cols);
this.consensus = new SimpleVector(rows);
br.readLine(); // skip "EIGENVALUES"
line = br.readLine(); // read eigenvalues
tok = new StringTokenizer(line);
for(int i = 0; i < cols; i++){
eigenValues[i] = Double.parseDouble(tok.nextToken());
}
br.readLine(); // skip "EIGENVECTORS | CONSENSUS"
for(int i = 0; i < rows; i++){
line = br.readLine();
tok = new StringTokenizer(line);
for(int j = 0; j < cols; j++){
eigenVectors.setElementValue(i, j, Double.parseDouble(tok.nextToken()));
}
consensus.setElementValue(i, Double.parseDouble(tok.nextToken())); // last entry is consensus
}
// try to read connectivity if exists
line = br.readLine();
if(line != null){
tok = new StringTokenizer(line);
tok.nextToken();
this.connectivity = new SimpleMatrix(Integer.parseInt(tok.nextToken()),Integer.parseInt(tok.nextToken()));
for(int i = 0; i < connectivity.getRows(); i++){
line = br.readLine();
tok = new StringTokenizer(line);
tok.nextToken(); // skip leading number of connections
for(int j = 0; j < connectivity.getCols(); j++){
connectivity.setElementValue(i, j, Double.parseDouble(tok.nextToken()));
}
}
}
br.close();
fr.close();
}
/**
* Writes the data in the class members to the file specified in filename.
*/
public void writeFile(){
assert( filename != null) : new Exception("Filename not set.");
assert( consensus != null && eigenVectors != null && eigenValues != null) : new Exception("Data not set.");
try {
System.out.println("Writing Eigen-Values and Eigen-Vectors to file: " + filename);
PrintWriter writer = new PrintWriter(filename,"UTF-8");
writer.println("DIMENSION " + this.eigenVectors.getRows() + " " + this.eigenVectors.getCols());
writer.println("POINTDIMENSION " + this.pointDimension);
writer.println("EIGENVALUES");
writer.println(buildStringEigenValues());
writer.println("EIGENVECTORS | CONSENSUS");
for(int i = 0; i < this.eigenVectors.getRows(); i++){
writer.println(buildStringEigenVectorAtIndex(i) + consensus.getElement(i));
}
//write connectivity if exists
if(connectivity != null){
writer.println("CONNECTIVITY " + connectivity.getRows() + " " +connectivity.getCols());
for(int i = 0; i < connectivity.getRows(); i++){
writer.println(buildStringConnectivity(i));
}
}
writer.close();
System.out.println("Finished writing.");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
/**
* Constructs the string object of connectivity-entries in the row specified for writing operation.
* @param idx The row index.
* @return The string containing the connectivity row.
*/
private String buildStringConnectivity(int idx){
StringBuilder ev = new StringBuilder();
ev.append(connectivity.getCols());
for(int i = 0; i < connectivity.getCols(); i++){
ev.append(" ");
ev.append(connectivity.getElement(idx, i));
}
return ev.toString();
}
/**
* Constructs the string object of Eigenvector-entries in the row specified for writing operation.
* @param idx The row index.
* @return The string containing the Eigenvector row.
*/
private String buildStringEigenVectorAtIndex(int idx){
StringBuilder ev = new StringBuilder();
for(int i = 0; i < eigenValues.length; i++){
ev.append(eigenVectors.getElement(idx, i));
ev.append(" ");
}
return ev.toString();
}
/**
* Constructs the string object of Eigenvalues for writing operation.
* @return The string containing the Eigenvalues.
*/
private String buildStringEigenValues(){
StringBuilder ev = new StringBuilder();
for(int i = 0; i < eigenValues.length; i++){
ev.append(eigenValues[i]);
ev.append(" ");
}
return ev.toString();
}
/**
* Sets the filename for IO operation.
* @param filename The file to be read or written.
*/
public void setFilename(String filename){
this.filename = filename;
}
/**
* Constructs the object and sets Eigenvalues, Eigenvectors and the consensus for writing operation.
* @param eVal
* @param eVec
* @param mean
*/
public PcaIO(int pointDim, double[] eVal, SimpleMatrix eVec, SimpleVector mean){
this.pointDimension = pointDim;
this.eigenValues = eVal;
this.eigenVectors = eVec;
this.consensus = mean;
}
/**
* Constructs the object and sets the filename, point dimension, eigenvalues, eigenvectors, mean shape and connectivity for writing.
* @param filename
* @param pca
*/
public PcaIO(String filename, PCA pca){
this.filename = filename;
this.pointDimension = pca.dimension;
this.eigenValues = pca.eigenValues;
this.eigenVectors = pca.eigenVectors;
this.consensus = pca.getConsensus();
this.connectivity = pca.connectivity;
}
/**
* Getter for the dimension of points. Needed if a point-cloud is analyzed using PCA.
* @return The point dimension.
*/
public int getPointDimension(){
return this.pointDimension;
}
/**
* Getter for the Eigenvalues after reading operation.
* @return The Eigenvalues.
*/
public double[] getEigenValues(){
return this.eigenValues;
}
/**
* Getter for the Eigenvectors after reading operation.
* @return The Eigenvectors.
*/
public SimpleMatrix getEigenVectors(){
return this.eigenVectors;
}
/**
* Getter for the connecivity after reading operation.
* @return The connectivity.
*/
public SimpleMatrix getConnectivity(){
return this.connectivity;
}
/**
* Getter for the consensus object after reading operation.
* @return The consensus object.
*/
public SimpleVector getConsensus(){
return this.consensus;
}
}