/**
* 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
* all attributes of the datum must be of the same type.
*
* 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
* 10 Dec 11 - keep datatype the same if all fields are same type
* kept byte order the same as input also;
* added -be switch
* 26 Sept 11 - 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;
//-------------- clamp and range variables ---------------------
// double is used regardless of type of data
static double minVal = Double.MIN_VALUE;
static double maxVal = Double.MAX_VALUE;
static int numMinClamp = 0;
static int numMaxClamp = 0;
static double minOver = Double.POSITIVE_INFINITY;
static double maxOver = Double.NEGATIVE_INFINITY;
static double minUnder = Double.POSITIVE_INFINITY;
static double maxUnder = Double.NEGATIVE_INFINITY;
static boolean clampValues = false;
static boolean outputLE = false;
static boolean outputBEflag = false;
//----------- type related values -----------------------
static int typeId = 0;
static String typeString = null;
// only 1 of the fields below will be used
static byte[] byteData;
static short[] shortData;
static int[] intData;
static float[] floatData;
static double[] doubleData;
//------------------------ main -------------------------------------
public static void main(String arg[])
throws FileNotFoundException, IOException
{
readArgs( arg );
DataSource dataSource = DataSource.create( "input", inName );
dataSource.activate();
RecordDescriptor rd = dataSource.getRecordDescriptor();
int nTypes = rd.getNumUsedTypes();
if ( nTypes == 1 )
{
typeId = rd.getStorageType( 0 );
typeString = rd.getFieldTypes()[ 0 ];
}
else
{
System.err.println( "Data fields of mixed type, all -> float!!" );
typeId = StorageType.Float;
typeString = "float";
}
ByteOrder order = ((PhysicalDataSource)dataSource).getByteOrder();
if ( order == ByteOrder.LITTLE_ENDIAN )
{
// if input is Little endian, so will output -- unless the
// -be flag is specified
if ( outputBEflag )
outputLE = false;
else
outputLE = true;
}
int nAttr = dataSource.getNumAttributes();
if ( nAttr > 1 && clampValues )
System.err.println( "****************** WARNING ******************\n"
+ " Data clamping option probably doesn't make sense with\n"
+ " multi-attribute files!!!" );
System.err.println(
"Converting data source " + inName + "\n"
+ " in bounds " + dataSource.getBounds () );
DataBlock db = getData( dataSource );
System.err.println( " 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 -------------------------------
/**
* get the subblock of data desired
*/
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 + dim ];
}
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 ));
switch ( typeId )
{
case StorageType.Byte:
byteData = block.getBytes();
writeData( out, byteData );
break;
case StorageType.Short:
shortData = block.getShorts();
writeData( out, shortData );
break;
case StorageType.Int:
intData = block.getInts();
writeData( out, intData );
break;
case StorageType.Float:
floatData = block.getFloats();
writeData( out, floatData );
break;
case StorageType.Double:
doubleData = block.getDoubles();
writeData( out, doubleData );
break;
default:
System.err.println( "ERROR: storage type: " + typeString +
" not supported by this program (yet)." );
System.exit( -1 );
break;
}
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( out, double[] )-------------------
/**
* Write the double data to the file.
*/
static void writeData( DataOutputStream out, double[] data )
throws IOException
{
ByteBuffer bb = ByteBuffer.allocate( data.length * 8 );
DoubleBuffer dataBuf = bb.asDoubleBuffer();
if ( clampValues )
{
// clamp values < min and greater than max
for ( int i = 0; i < data.length; i++ )
{
data[ i ] = clamp( data[ i ] );
}
}
printMinMax( data );
dataBuf.put( data );
byte[] bbData = bb.array();
if ( outputLE )
swapBytes( bbData, 8 );
out.write( bbData );
}
//----------------- writeData( out, float[] ) ------------------------
/**
* Write the float data to the file.
*/
static void writeData( DataOutputStream out, float[] data )
throws IOException
{
ByteBuffer bb = ByteBuffer.allocate( data.length * 4 );
FloatBuffer dataBuf = bb.asFloatBuffer();
if ( clampValues )
{
// clamp values < min and greater than max
for ( int i = 0; i < data.length; i++ )
{
data[ i ] = (float) clamp( data[ i ] );
}
}
printMinMax( data );
dataBuf.put( data );
byte[] bbData = bb.array();
if ( outputLE )
swapBytes( bbData, 4 );
out.write( bbData );
}
//----------------- writeData( out, int[] )-------------------
/**
* Write the int data to the file.
*/
static void writeData( DataOutputStream out, int[] data )
throws IOException
{
ByteBuffer bb = ByteBuffer.allocate( data.length * 4 );
IntBuffer dataBuf = bb.asIntBuffer();
if ( clampValues )
{
// clamp values < min and greater than max
for ( int i = 0; i < data.length; i++ )
{
data[ i ] = (int)clamp( data[ i ] );
}
}
printMinMax( data );
dataBuf.put( data );
byte[] bbData = bb.array();
if ( outputLE )
swapBytes( bbData, 4 );
out.write( bbData );
}
//----------------- writeData( out, short[] )-------------------
/**
* Write the short data to the file.
*/
static void writeData( DataOutputStream out, short[] data )
throws IOException
{
ByteBuffer bb = ByteBuffer.allocate( data.length * 2 );
ShortBuffer dataBuf = bb.asShortBuffer();
if ( clampValues )
{
// clamp values < min and greater than max
for ( int i = 0; i < data.length; i++ )
{
data[ i ] = (short)clamp( data[ i ] );
}
}
printMinMax( data );
dataBuf.put( data );
byte[] bbData = bb.array();
if ( outputLE )
swapBytes( bbData, 2 );
out.write( bbData );
}
//----------------- writeData( out, byte[] )-------------------
/**
* Write the byte data to the file.
*/
static void writeData( DataOutputStream out, byte[] data )
throws IOException
{
ByteBuffer bb = ByteBuffer.allocate( data.length );
byte[] bbData = bb.array();
for ( int i = 0; i < data.length; i++ )
{
if ( clampValues )
bbData[ i ] = (byte)clamp( data[ i ] );
else
bbData[ i ] = data[ i ];
}
printMinMax( bbData );
out.write( bbData );
}
//----------------- clamp( double ) ------------------------------------
/**
* Clamp a value to desired range and gather stats for the clamping
*/
static double clamp( double val )
{
if ( val < minVal )
{
numMinClamp++;
if ( val > maxUnder )
maxUnder = val;
if ( val < minUnder )
minUnder = val;
val = minVal;
}
else if ( val > maxVal )
{
numMaxClamp++;
if ( val > maxOver )
maxOver = val;
if ( val < minOver )
minOver = val;
val = maxVal;
}
return val;
}
//----------------- printMinMax( byte[] ) ------------------------------
static void printMinMax( byte[] data )
{
byte min = Byte.MAX_VALUE;
byte max = Byte.MIN_VALUE;
for ( int i = 0; i < data.length; i++ )
{
if ( data[ i ] < min ) min = data[ i ];
if ( data[ i ] > max ) max = data[ i ];
}
System.err.println( "Output min, max: " + min + " ---> " + max );
}
//----------------- printMinMax( short[] ) ------------------------------
static void printMinMax( short[] data )
{
short min = Short.MAX_VALUE;
short max = Short.MIN_VALUE;
for ( int i = 0; i < data.length; i++ )
{
if ( data[ i ] < min ) min = data[ i ];
if ( data[ i ] > max ) max = data[ i ];
}
System.err.println( "Output min, max: " + min + " ---> " + max );
}
//----------------- printMinMax( int[] ) ------------------------------
static void printMinMax( int[] data )
{
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for ( int i = 0; i < data.length; i++ )
{
if ( data[ i ] < min ) min = data[ i ];
if ( data[ i ] > max ) max = data[ i ];
}
System.err.println( "Output min, max: " + min + " ---> " + max );
}
//----------------- printMinMax( float[] ) ------------------------------
static void printMinMax( float[] data )
{
float min = Float.MAX_VALUE;
float max = Float.MIN_VALUE;
for ( int i = 0; i < data.length; i++ )
{
if ( data[ i ] < min ) min = data[ i ];
if ( data[ i ] > max ) max = data[ i ];
}
System.err.println( "Output min, max: " + min + " ---> " + max );
}
//----------------- printMinMax( double[] ) ------------------------------
static void printMinMax( double[] data )
{
double min = Double.MAX_VALUE;
double max = Double.MIN_VALUE;
for ( int i = 0; i < data.length; i++ )
{
if ( data[ i ] < min ) min = data[ i ];
if ( data[ i ] > max ) max = data[ i ];
}
System.err.println( "Output min, max: " + min + " ---> " + max );
}
//----------------- swapBytes -----------------------------------------
/**
* Swap bytes to reverse endianness
*/
static void swapBytes( byte[] data, int bytesPerVal )
{
byte temp;
for ( int i = 0; i < data.length; i += bytesPerVal )
{
for ( int j = 0; j < bytesPerVal / 2; j++ )
{
temp = data[ i + j ];
data[ i + j ] = data[ i + bytesPerVal - j - 1 ];
data[ i + bytesPerVal - j - 1 ] = temp;
}
}
}
//---------------------- 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( " -le write the binary output data as little endian" );
out.println( " default is same as input." );
out.println( " -be write the binary output data as big endian" );
out.println( " default is same as input." );
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: Multiple attributes of different types are not " +
"supported -- except by converting all to float!");
}
//-------------- 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 ( sarg.equalsIgnoreCase( "-le" ))
outputLE = true;
else if ( sarg.equalsIgnoreCase( "-be" ))
outputBEflag = true;
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 if ( inName == null ) // check for file name
inName = args[ iarg ];
else if ( baseName == null )
baseName = args[ iarg ];
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;
}
}