//******************************************************************************
//
// File: CreateSignalDataSet.java
// Package: edu.rit.mri
// Unit: Class edu.rit.mri.CreateSignalDataSet
//
// This Java source file is copyright (C) 2008 by Alan Kaminsky. All rights
// reserved. For further information, contact the author, Alan Kaminsky, at
// ark@cs.rit.edu.
//
// This Java source file is part of the Parallel Java Library ("PJ"). PJ is free
// software; you can redistribute it and/or modify it under the terms of the GNU
// General Public License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// PJ 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 General Public License for more details.
//
// A copy of the GNU General Public License is provided in the file gpl.txt. You
// may also obtain a copy of the GNU General Public License on the World Wide
// Web at http://www.gnu.org/licenses/gpl.html.
//
//******************************************************************************
package edu.rit.mri;
import edu.rit.numeric.ListSeries;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Scanner;
/**
* Class CreateSignalDataSet is a preprocessing program that reads a magnetic
* resonance image stored in a group of files in a certain format and creates a
* spin signal data set file. This spin signal data set file is then used by the
* other programs in package edu.rit.mri.
*
* Usage: java edu.rit.mri.CreateSignalDataSet timesfile maskfile
* signalfile
*
timesfile = Input times file
*
maskfile = Input mask file
*
signalfile = Output spin signal data set file
*
* The program's error handling is rudimentary. If an error is encountered, the
* program throws an exception and terminates.
*
* The format of the output spin signal data set file is described in class
* {@linkplain SignalDataSetWriter}. The formats of the input files are
* described below.
*
* Times file. The times file is a plain text file giving the image
* dimensions in pixels and the list of discrete time values at which the images
* were taken. Each line of the times file consists of fields separated by white
* space. The first line of the times file has two fields, the image height
* H (integer) and the image width W (integer). The rest of the
* times file consists of M lines. The first field on each line gives
* ti, the time (sec, real number) at which measurement
* i was taken, 0 ≤ i ≤ M−1. The second field
* on each line gives the name of the data file for measurement i.
*
* Data files. Each data file is a binary file containing the pixel data
* values for a certain measurement i taken at time
* ti. The file contains H×W spin
* signal values Src(ti),
* where r is the pixel's row in the range 0 ≤ r ≤
* H−1 and c is the pixel's column in the range 0 ≤
* c ≤ W−1. The pixel data values are stored in row major
* order -- first the pixels in the topmost row from left to right, then the
* pixels in the second row from left to right, and so on.
*
* Each pixel data value is stored in binary as a two-byte integer in the range
* −32768 ≤ Src(ti)
* ≤ 32767. The first two pixels (S00 and
* S01) do not contain actual data, but instead are set to the
* values S00 = 0 and S01 = 1. These are
* used to determine the byte order or endianness with which the pixel
* data values are stored. If the first four bytes of the data file are 0, 0, 0,
* 1, then the pixel data values are stored in big-endian order -- most
* significant byte first. If the first four bytes of the data file are 0, 0, 1,
* 0, then the pixel values are stored in little-endian order -- least
* significant byte first. It is assumed that all data files use the same byte
* order.
*
* Mask file. The mask file is a binary file containing
* H×W mask values Mrc.
* Mrc is 1 if pixel (r,c) has data to be
* analyzed. Mrc is 0 if pixel (r,c) is not to
* be analyzed.
*
* Each mask value is stored in binary as a two-byte integer. The first pixel
* (M00) does not contain actual mask data, but instead is set
* to the value M00 = 1. This is used to determine the mask
* values' byte order. If the first two bytes of the mask file are 0, 1, then
* the mask values are stored in big-endian order. If the first two bytes of the
* mask file are 1, 0, then the mask values are stored in little-endian order.
*
* @author Alan Kaminsky
* @version 21-Jun-2008
*/
public class CreateSignalDataSet
{
// Prevent construction.
private CreateSignalDataSet()
{
}
// Main program.
/**
* Main program.
*/
public static void main
(String[] args)
throws Exception
{
byte b1, b2, b3, b4;
// Parse command line arguments.
if (args.length != 3) usage();
File timesfile = new File (args[0]);
File maskfile = new File (args[1]);
File signalfile = new File (args[2]);
// Read times file: height, width, time series, and list of data files.
Scanner timesScanner = new Scanner (timesfile);
Scanner lineScanner = new Scanner (timesScanner.nextLine());
int H = lineScanner.nextInt();
int W = lineScanner.nextInt();
int P = H*W;
ListSeries t_series = new ListSeries();
ArrayList datafile_list = new ArrayList();
while (timesScanner.hasNextLine())
{
lineScanner = new Scanner (timesScanner.nextLine());
t_series.add (lineScanner.nextDouble());
datafile_list.add (new File (lineScanner.next()));
}
timesScanner.close();
int M = t_series.length();
// Read mask file.
boolean[] mask = new boolean [P];
ByteBuffer buf = ByteBuffer.allocateDirect (2*P);
fillBuffer (buf, maskfile);
b1 = buf.get();
b2 = buf.get();
if (b1 == 0 && b2 == 1)
{
buf.order (ByteOrder.BIG_ENDIAN);
}
else if (b1 == 1 && b2 == 0)
{
buf.order (ByteOrder.LITTLE_ENDIAN);
}
else
{
throw new IOException
("Illegal start of mask file \""+maskfile+"\": "+b1+", "+b2);
}
for (int i = 1; i < P; ++ i)
{
short val = buf.getShort();
if (val == 0)
{
mask[i] = false;
}
else if (val == 1)
{
mask[i] = true;
}
else
{
throw new IOException
("Illegal value "+val+" for pixel "+i+" in mask file \""+
maskfile+"\"");
}
}
// Set up arrays to hold pixel signal data.
short[][] S_array = new short [P] [];
for (int i = 0; i < P; ++ i)
{
if (mask[i])
{
S_array[i] = new short [M];
}
}
// Read data files.
for (int j = 0; j < M; ++ j)
{
File datafile = datafile_list.get(j);
fillBuffer (buf, datafile);
b1 = buf.get();
b2 = buf.get();
b3 = buf.get();
b4 = buf.get();
if (b1 == 0 && b2 == 0 && b3 == 0 && b4 == 1)
{
buf.order (ByteOrder.BIG_ENDIAN);
}
if (b1 == 0 && b2 == 0 && b3 == 1 && b4 == 0)
{
buf.order (ByteOrder.LITTLE_ENDIAN);
}
else
{
throw new IOException
("Illegal start of data file \""+datafile+"\": "+b1+", "+
b2+", "+b3+", "+b4);
}
for (int i = 2; i < P; ++ i)
{
short val = buf.getShort();
if (mask[i])
{
S_array[i][j] = val;
}
}
}
// Write height, width, and time series to spin signal data set.
SignalDataSetWriter writer =
new SignalDataSetWriter (signalfile, H, W, t_series);
// Write pixel signal data to spin signal data set.
for (int i = 0; i < P; ++ i)
{
if (mask[i])
{
writer.addPixelSignal (new PixelSignal (0, i, S_array[i]));
}
}
// All done!
writer.close();
}
// Hidden operations.
/**
* Fill the given buffer with the contents of the given file.
*/
private static void fillBuffer
(ByteBuffer buf,
File file)
throws IOException
{
FileInputStream fis = new FileInputStream (file);
FileChannel fchan = fis.getChannel();
buf.clear();
while (buf.hasRemaining())
{
if (fchan.read (buf) == -1)
{
throw new EOFException
("Unexpected EOF in file \""+file+"\"");
}
}
fis.close();
buf.flip();
}
/**
* Print a usage message and exit.
*/
private static void usage()
{
System.err.println ("Usage: java edu.rit.mri.CreateSignalDataSet ");
System.err.println (" = Input times file");
System.err.println (" = Input mask file");
System.err.println (" = Output spin signal data set file");
System.exit (1);
}
}
Ads help maintain this website.