/**
* SubFile -- read a Granite file; generate a new one that is a subblock
* of the input file. The input data can be of any type,
* but for now, the output is always float.
* Arguments: xfdlfile - input data set
* outNameBase - a string to be used as the prefix for the
* 2 output files; .bin and .xfdl
* bounds - subblock specification
*
* 22-23 Sept 2011 (derived from SplitData.java and BasicDemo.java)
* rdb
* ------------------
* History
* 26 Sept 11 - rdb added run time option for eliminating meaningless
* values. The bunny data uses -1200 to identify coord
* positions that seem to be outside the actual data area.
* The bunny and other dicomed formatted data shows a
* circular data region (such as when viewed by OsirisX).
* Map those values to 0. Since I don't know whether -1200
* is just the bunny convention or a more common one, I've
* decided to allow the user to specify min and/or max
* values; all values outside the range are clamped to
* min or max.
*/
import edu.unh.sdb.common.*;
import edu.unh.sdb.datasource.*;
import java.io.*;
import java.nio.*;
import java.util.*;
public class SubFile
{
//----------------- class variables -----------------------------
static String inName = null;
static String baseName = null;
static int[] boundsArgs = null;
static float minVal = Float.MIN_VALUE;
static float maxVal = Float.MAX_VALUE;
static int numMinClamp = 0;
static int numMaxClamp = 0;
static float minOver = Float.POSITIVE_INFINITY;
static float maxOver = Float.NEGATIVE_INFINITY;
static float minUnder = Float.POSITIVE_INFINITY;
static float maxUnder = Float.NEGATIVE_INFINITY;
static boolean clampValues = false;
public static void main(String arg[])
throws FileNotFoundException, IOException
{
readArgs( arg );
DataSource dataSource = DataSource.create( "input", inName );
dataSource.activate();
System.err.println(
"Converting data source " + inName + "\n"
+ " in bounds " + dataSource.getBounds () );
DataBlock db = getData( dataSource );
System.err.println( " to float, out bounds " + db.getBounds() );
writeFile( db, baseName );
if ( numMinClamp > 0 )
{
System.err.println( "minClamps: " + numMinClamp + " from " +
minUnder + " to " + maxUnder );
}
if ( numMaxClamp > 0 )
{
System.err.println( "maxClamps: " + numMaxClamp + " from " +
minOver + " to " + maxOver );
}
}
//------------------- getData -------------------------------
private static DataBlock getData( DataSource inDS )
{
int dim = inDS.dim();
ISBounds dsb = inDS.getBounds();
if ( boundsArgs != null )
{
int[] lb = new int[ dim ];
int[] ub = new int[ dim ];
if ( boundsArgs.length == dim )
{
for ( int i = 0; i < dim; i++ )
{
lb[ i ] = 0;
ub[ i ] = boundsArgs[ i ] - 1;
}
dsb = new ISBounds( lb, ub );
}
else if ( boundsArgs.length == 2 * dim )
{
for ( int i = 0; i < dim; i++ )
{
lb[ i ] = boundsArgs[ i ];
ub[ i ] = boundsArgs[ i + 3 ];
}
dsb = new ISBounds( lb, ub );
}
else
{
System.err.println( "Error in bounds args: must have " +
dim + " bounds or " + (2*dim) + " bounds. " +
boundsArgs.length + " found. Using NO subbounds." );
}
}
DataBlock db = inDS.subblock( dsb );
return db;
}
//-------------- writeFile ------------------------------------
private static void writeFile( DataBlock block, String base )
throws IOException
{
ISBounds bnds = block.getBounds();
int lower[] = bnds.copyLowerArray();
int upper[] = bnds.copyUpperArray();
String fileName = base + ".bin";
DataOutputStream out = new DataOutputStream(
new FileOutputStream( fileName ));
float data[] = block.getFloats();
writeData( out, data );
out.close();
//-------------------------------------------------------
// Now write xfdl file
//-----------------------------
PrintWriter pw = new PrintWriter(
new FileOutputStream( base + ".xfdl" ));
pw.println( "" );
pw.println( "" );
pw.println( "" );
/** Granite doesn't seem to handle files with non-zero lower
bounds, so following output (which should be ok) doesn't work
for ( int i = 0; i < lower.length; i++ )
{
pw.println( "" );
}
*******************************/
// Instead just always have bounds go from 0 to size-1
for ( int i = 0; i < lower.length; i++ )
{
pw.println( "" );
}
// Now do the fieldNames and types:
RecordDescriptor rd = block.getRecordDescriptor ();
String[] fieldNames = rd.getFieldNames ();
String[] fieldTypes = rd.getFieldTypes ();
for (int i = 0; i < fieldNames.length; i++)
pw.println ( " " );
// and finish up
pw.println( "" );
pw.close();
}
//----------------- writeData ---------------------------------------
/**
* Write the data to the file.
*/
static void writeData( DataOutputStream out, float[] data )
throws IOException
{
ByteBuffer bb = ByteBuffer.allocate( data.length * 4 );
FloatBuffer fb = bb.asFloatBuffer();
if ( clampValues )
{
// clamp values < min and greater than max
for ( int i = 0; i < data.length; i++ )
{
if ( data[ i ] < minVal )
{
numMinClamp++;
if ( data[ i ] > maxUnder )
maxUnder = data[ i ];
if ( data[ i ] < minUnder )
minUnder = data[ i ];
data[i] = minVal;
}
else if ( data[ i ] > maxVal )
{
numMaxClamp++;
if ( data[ i ] > maxOver )
maxOver = data[ i ];
if ( data[ i ] < minOver )
minOver = data[ i ];
data[i] = maxVal;
}
}
}
fb.put( data );
byte[] bbData = bb.array();
out.write( bbData );
}
//---------------------- openDataSource ----------------------------
// a little utility function to read the descriptor file.
//
private static DataSource openDataSource( String dsName, String fileName )
{
DataSource ds = null;
try
{
ds = DataSource.create( dsName, inName );
} catch ( Exception ex )
{
System.err.println( "Open error: " + ex.getMessage() );
System.exit( -1 );
}
return ds;
}
//--------------- usage -----------------------------------
static void usage()
{
PrintStream out = System.out; // should pass in output stream
out.println( "Usage: java SubFile infdl outPrefix [-min v1] [-max v2] [ bnds ]" );
out.println( " infdl - xfdl file describing the input DataSource" );
out.println( " outPrefix - prefix for output file names" );
out.println();
out.println( "-min, -max: optional values for clamping input values" );
out.println( " bnds subblock of the DataSource as block input." );
out.println( " bnds must be either n ints or 2n ints where" );
out.println( " n is the dim of DataSource. if n values");
out.println( " specified, they define SIZES of bounds ");
out.println( " with 0,0,0 as origin of subblock");
out.println( " if not specified, copies the entire file");
out.println( "");
out.println( "NOTE: output DataSource always float; so no bounds");
out.println( " can be used to convert an int/short ds to floats");
}
//-------------- readArgs --------------------------------
//
static void readArgs( String[] args )
{
if ( args.length == 0 )
{
usage();
System.exit( 0 );
}
ArrayList intArgs = new ArrayList();
for ( int iarg = 0; iarg < args.length; iarg++ )
{
String sarg = args[ iarg ];
if ( inName == null ) // check for file name
inName = args[ iarg ];
else if ( baseName == null )
baseName = args[ iarg ];
else if ( sarg.equalsIgnoreCase( "-min" ))
{
minVal = getArg( args, ++iarg, Float.MIN_VALUE );
clampValues = true;
}
else if ( sarg.equalsIgnoreCase( "-max" ))
{
maxVal = getArg( args, ++iarg, Float.MAX_VALUE );
clampValues = true;
}
else // read bounds arguments
intArgs.add( getArg( args, iarg, 0 ));
}
if ( intArgs.size() > 0 )
{
boundsArgs = new int[ intArgs.size() ];
for ( int i = 0; i < intArgs.size(); i++ )
boundsArgs[i] = ((Integer)intArgs.get( i )).intValue();
}
}
//---------------------- getArg( String[], int, int ) -----------------
/**
* This is a utility method for accessing one of the command line
* string arguments and converting it to an int. It accepts the
* entire array of arguments in String form, an integer indicating
* which is to be converted to an int and a default value to be
* returned if this argument was not on the command line, or if
* the specified String is not a valid integer representation.
*/
public static int getArg( String[ ] args, int which, int defaultVal )
{
try
{
return Integer.parseInt( args[ which ] );
}
catch ( ArrayIndexOutOfBoundsException oob )
{
// If there is no args[ which ] element, Java "throws" an exception.
// This code "catches" the exception and handles it gracefull.
// In this case, it is not an error. The parameter is optional
// and there is a specified default value that is returned.
}
catch ( NumberFormatException nfe )
{
// If the string is not a valid representation of an integer
// (such as 4Qd3), Integer.parseInt throws a NumberFormatException
// Again, this code catches the exception, gives an error message
// and uses the default value.
System.err.println( "Error: improper command line argument " + which + " = " + args[ which ]
+ ". It should be an integer; using default value: "
+ defaultVal );
}
return defaultVal;
}
//---------------------- getArg( String[], int, float ) -----------------
/**
* float version of getArg
*/
public static float getArg( String[ ] args, int which, float defaultVal )
{
try
{
return Float.parseFloat( args[ which ] );
}
catch ( ArrayIndexOutOfBoundsException oob )
{
}
catch ( NumberFormatException nfe )
{
System.err.println( "Error: improper command line argument "
+ which + " = " + args[ which ]
+ ". It should be an float; using default value: "
+ defaultVal );
}
return defaultVal;
}
//---------------------- getArg( String[], int, String ) -----------------
/**
* String version of getArg
*/
public static String getArg( String[ ] args, int which, String defaultVal )
{
try
{
return args[ which ];
}
catch ( ArrayIndexOutOfBoundsException oob )
{
}
return defaultVal;
}
}