/*
* Copyright (C) 2010-2014 Andreas Maier
* CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
*/
package edu.stanford.rsl.conrad.io;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import edu.stanford.rsl.conrad.data.numeric.Grid2D;
import edu.stanford.rsl.conrad.utils.CONRAD;
import ij.IJ;
import ij.ImagePlus;
import ij.io.FileInfo;
import ij.process.ImageProcessor;
/**
* Class to stream SEQ Format. Created with a lot of help from Jared Starman.
*
* @author akmaier
*
*/
public class SEQProjectionSource extends FileProjectionSource {
protected boolean debug = true;
protected float [] lut;
protected int [] Inlut;
private boolean directoryMode;
private ArrayList<File> fileList;
private void initLUT() {
lut = new float[65537];
for (int i=0;i<= 16383;i++){
lut[i+1] = ((float)i);
}
for (int i=0;i<= 16383;i++){
lut[i+1+16384] = ((float)i)*2;
}
for (int i=0;i<= 16383;i++){
lut[i+1+32768] = ((float)i)*4;
}
for (int i=0;i<= 16383;i++){
lut[i+1+49152] = ((float)i)*8;
}
if (fi!=null && fi.width>0 && fi.height>0) {
init();
} else {
throw new RuntimeException("Format does not match: width = " + fi.width + " height = " + fi.height + " offset = " + fi.offset);
}
}
@Override
public void initStream (String filename) throws IOException {
File file = new File(filename);
if (file.isDirectory()){
// multi file format
fileList = new ArrayList<File>();
String [] files = file.list();
Arrays.sort(files);
for (String f: files){
if (f.substring(f.length()-4).compareToIgnoreCase(".viv")==0){
fileList.add(new File(file.getAbsolutePath() + "/" +f));
}
}
if (fileList.size() == 0) throw new IOException("Directory does not contain .viv files");
fi = getHeaderInfo(fileList.get(0).getAbsolutePath());
initLUT();
directoryMode = true;
} else {
// Code for single file
fi = getHeaderInfo(filename);
initLUT();
}
}
/**
* Reads the header information from the file into a fileinfo object
* @param filename the filename
* @return the FileInfo
* @throws IOException
*/
public FileInfo getHeaderInfo( String filename ) throws IOException {
if (IJ.debugMode) CONRAD.log("Entering Nrrd_Reader.readHeader():");
FileInfo fi = new FileInfo();
File file = new File(filename);
fi.fileName=file.getName();
fi.directory = file.getParent() + "/";
// NB Need RAF in order to ensure that we know file offset
RandomAccessFile input = new RandomAccessFile(fi.directory+fi.fileName,"r");
fi.fileType = FileInfo.GRAY8; // just assume this for the mo
fi.fileFormat = FileInfo.RAW;
fi.nImages = 1;
// parse the header file, until reach an empty line// boolean keepReading=true;
byte [] values = new byte[4];
input.read(values);
fi.offset = (int) convertToUnsignedInt(values,0);
//System.out.println("Read " + fi.offset);
if (fi.offset != 2048){
throw new IOException("Wrong Header Size; Not an SEQ File.");
}
byte [] header = new byte [(fi.offset) -4];
input.read(header);
fi.width = (int) convertToUnsignedInt(header, 3);
fi.height = (int) convertToUnsignedInt(header, 4);
fi.nImages = (int) convertToUnsignedInt(header, 6);
if (true) {
System.out.println("SEQ Reading image with " + fi.nImages + " frames with " + fi.width + "x" + fi.height + " resolution");
}
int dataType = (int) convertToUnsignedInt(header, 458);
if((dataType & 512)>0){
fi.compression = FileInfo.COMPRESSION_UNKNOWN;
}
else {
fi.compression = FileInfo.COMPRESSION_NONE;
}
fi.fileType=FileInfo.GRAY16_UNSIGNED;
fi.intelByteOrder = true;
input.close();
return (fi);
}
private Grid2D readSingleImage(){
Grid2D revan = super.getNextProjection();
if (revan != null) {
try {
Thread.sleep(CONRAD.INPUT_QUEUE_DELAY);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Grid2D uncompressed = new Grid2D(revan.getWidth(), revan.getHeight());
// uncompression
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for (int i = 0; i< revan.getWidth(); i++){
for (int j = 0; j< revan.getHeight(); j++){
int val = (int) (revan.getPixelValue(i, j));
if (val > max) max = val;
if (val < min) min = val;
if (fi.compression == FileInfo.COMPRESSION_UNKNOWN){
uncompressed.putPixelValue(i,j,lut[val+1]);
}
else {
uncompressed.putPixelValue(i,j,val);
}
}
}
//System.out.println("Range: " + min + " " + max);
return uncompressed;
} else {
return null;
}
}
@Override
public Grid2D getNextProjection(){
if (!directoryMode) {
return readSingleImage();
} else {
try{
fi = getHeaderInfo(fileList.get(currentIndex+1).getPath());
initStack();
return readSingleImage();
} catch (IOException e) {
e.printStackTrace();
return null;
} catch (IndexOutOfBoundsException e){
return null;
}
}
}
public void saveViva(ImagePlus imp, String path) {
if (imp.getNSlices()<1) return;
ImageProcessor improc = imp.getProcessor();
File file = new File(path);
DataOutputStream dos = null;
initInLUT();
try {
dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
FileInfo fi = imp.getFileInfo();
dos.write(convertInt2Bytes(2048));
dos.writeChars("RevK04");
dos.write(convertInt2Bytes(fi.width));
dos.write(convertInt2Bytes(fi.height));
dos.write(convertInt2Bytes(2));
dos.write(convertInt2Bytes(fi.nImages));
byte [] a = {-93,46,90,-36,-50,-73,17,-45,-94,-24,1,16,75,-58,-41,-51,0,0,0,0};
dos.write(a);
dos.write(ByteBuffer.allocate(1784).array());
int dataType = 0;
if (improc.getMax()>65535) dataType = 514;
dos.write(convertInt2Bytes(dataType));
dos.write(ByteBuffer.allocate(208).array());
byte[] bBuff = new byte [imp.getWidth()*imp.getHeight()*2];
for (int j=0;j<imp.getHeight();j++){
for (int k=0;k<imp.getWidth();k++){
float val = (float) (improc.getPixelValue(k, j));
if (dataType == 514){
bBuff[j*imp.getWidth()*2+k*2] = convertInt2UnShort(Inlut[(int)val+1])[0];
bBuff[j*imp.getWidth()*2+k*2+1] = convertInt2UnShort(Inlut[(int)val+1])[1];
}
else {
bBuff[j*imp.getWidth()*2+k*2] = convertInt2UnShort((int)val)[0];
bBuff[j*imp.getWidth()*2+k*2+1] = convertInt2UnShort((int)val)[1];
}
}
}
dos.write(bBuff);
dos.flush();
dos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private void initInLUT() {
Inlut = new int [131073];
for (int i=0;i<= 16383;i++){
Inlut[i+1] = (int)i;
}
for (int i=16384;i<= 32767;i++){
Inlut[i+1] = (int)(i/2 + 16384);
}
for (int i=32768;i<= 49151;i++){
Inlut[i+1] = (int)(i/4 + 32768);
}
for (int i=49152;i<= 131071;i++){
Inlut[i+1] = (int)(i/8 + 49152);
}
}
public byte[] convertInt2Bytes(int value) {
byte[] buf = new byte[4];
buf[3] = (byte) ((value & 0xFF000000)>>24);
buf[2] = (byte) ((value & 0x00FF0000)>>16);
buf[1] = (byte) ((value & 0x0000FF00)>>8);
buf[0] = (byte) (value & 0x000000FF);
return buf;
}
public byte[] convertInt2UnShort(int value) {
byte[] buf = new byte[2];
buf[1] = (byte) ((value & 0x0000FF00)>>8);
buf[0] = (byte) (value & 0x000000FF);
return buf;
}
}