#define VERSION "2.33.1"

//****************************************************************************
//
// Modification History (most recent first)
// mm/dd/yy  Who  What
//
// 10/04/06       Add DiniftiOptions class and verbose mode
//                (contributed by Michael Hanke)
// 08/15/06  VPL  Fix bugs in output
//                Make software GPL
//                Format --help and --version to be in standard form
// 03/22/05  VPL  Allow processing of multiple series
//                Add NYU License agreement
// 01/24/05  VPL  
//
//****************************************************************************

#include <dirent.h>
#include <sstream>
#include <algorithm>
#include "dinifti.h"
#include "niftiout.h"

//****************************************************************************
//
// Purpose: Comaprison of image entries
//   
// Parameters: DICOMImage lhs - left-hand side item
//             DICOMImage rhs - left-hand side item
//   
// Returns: 0 if rhs >= lhs
//   
// Notes: 
//
//****************************************************************************

static int CompareImages(const DICOMImage &lhs, const DICOMImage &rhs)
{
	float lhsValue = lhs.SagPos() * lhs.SagNorm() + lhs.CorPos() * lhs.CorNorm() + lhs.TraPos() * lhs.TraNorm();
	
	float rhsValue = rhs.SagPos() * rhs.SagNorm() + rhs.CorPos() * rhs.CorNorm() + rhs.TraPos() * rhs.TraNorm();

	int retValue = 0;
	
	// If position the same, use image number to sort

	if (fabs(lhsValue - rhsValue) > 0.000001)
		retValue = lhsValue < rhsValue;
	else
		retValue = lhs.ImageNumber() < rhs.ImageNumber();
	
	return retValue;
}

//****************************************************************************
//
// Purpose: Compose file name from pieces and open for writing
//   
// Parameters: char *&fname           - where we store the name
//             const char  *name      - Root of output file
//             OUTPUT_TYPE form       - NIfTI output format
//             bool        compressed - write compressed
//             bool        header     - if true (default) then headr file, else
//                                      image file
//   
// Returns: FILE *fp - pointer to open FILE
//   
// Notes: 
//
//****************************************************************************

static void SetFileName(char *&fname, const char *name, OUTPUT_TYPE form, bool compressed, bool header = true)
{
	ostringstream outFileName(name, ostringstream::app);
	
	if (header)
		switch( form )
		{
		    case NIfTI_SINGLE:
				outFileName << ".nii";
				break;
		    case NIfTI_ASCII:
				outFileName << ".nia";
				break;
		    case ANALYZE:
		    case NIfTI_DUAL:
				outFileName << ".hdr";
				break;
		}
	else
		outFileName << ".img";

	if (compressed)
		outFileName << ".gz";

	fname = new char [outFileName.str().length() + 1];
	strcpy(fname, outFileName.str().c_str());
}

//****************************************************************************
//
// Purpose: Generate output name based on format string
//   
// Parameters: DICOMImage *dcmImg - current DICOM image
//             char *format       - format string
//             std::string &name  - where to append the format
//   
// Returns: NONE
//   
// Notes: 
//
//****************************************************************************

static void FormattedName(DICOMImage *dcmImg, char *format, std::string &name)
{
	// Loop through characters in format and process accordingly
	char *cp = format;
	while ( *cp != '\0' )
	{
		if ( *cp == '%' )
		{
			++cp;
			switch ( *cp )
			{
				case 'I':
					name += dcmImg->SubjectID();
					break;
				case 'N':
					name += dcmImg->SubjectName();
					break;
				case 'S':
					name += dcmImg->SeriesNumber();
					break;
				case 'D':
					name += dcmImg->SeriesDescription();
					break;
				case 'Y':
					name += dcmImg->SeriesDate();
					break;
				case 'T':
					name += dcmImg->SeriesTime();
					break;
				case 'A':
					name += dcmImg->Accession();
					break;
				case 'C':
					name += dcmImg->Comments();
					break;
				case '%':
					name += '%';
					break;
				default:
					// Who knows, ignore it
					break;
			}
		}
		else
		{
			// Just append the character to the string
			// But use '~' instead of '~'
			if ( *cp == '/' )
				name += '~';
			else
				name += *cp;
		}
		++cp;
	}
}

//****************************************************************************
//
// Purpose: Display usage and leave
//   
// Parameters: char **argv - standard arguments
//   
// Returns: NONE
//   
// Notes: 
//
//****************************************************************************

void ShowHelp(char **argv)
{
	cout << "'dinifti' converts DICOM image file to NIfTI format" << endl << endl
		 << "Usage: " << argv[0] << " [OPTION] <DICOM input> <NIfTI output>" << endl << endl
		 << "Options:" << endl
		 << " -g                   compressed output" << endl
		 << " -f <output format>   'a2' - ANALYZE 7.5 Dual file" << endl
		 << "                      'n2' - NIfTI-2 Dual file" << endl 
		 << "                      'n1' - NIfTI-1 Single file **Default**" << endl 
		 << " -d                   append series description to output file name(s)" << endl
		 << " --name=\"<format>\"  format output file name(s) according to <format>" << endl
		 << "                      where <format> : " << endl
		 << "                                       %I = subject ID" << endl
		 << "                                       %N = subject name" << endl
		 << "                                       %S = series number" << endl
		 << "                                       %D = series description" << endl
		 << "                                       %Y = series date" << endl
		 << "                                       %T = series time" << endl
		 << "                                       %A = accession number" << endl
		 << "                                       %C = image comments" << endl		
		 << "                                       %% = percentage sign" << endl
		 << "                      Example: " << endl
		 << "                               --name=\"%Y:%T - %A - %I - %D(%S)\"" << endl
		 << "                      will result in name(s) formed like" << endl
		 << "                               date:time - accession - ID - description(number)" << endl
 		 << "                      N.B.: format string must be enclosed in quotes" << endl
		 << "                            if format is specified, -d flag is ignored" << endl
		 << "                            the reserved character \"/\" will be rendered as \"~\"" << endl
		 << " -s #                 number of slices per volume" << endl
		 << " -v --verbose    enable verbose status output" << endl
		 << " -n --noact      do all processing, but do not write files" << endl << endl
		 << " -h --help       print this help and exit" << endl
		 << " -V --version    print version number and exit" << endl << endl
		 << "I/O Options" << endl
		 << "<DICOM input> can be single file, list of files or directory" << endl
		 << "<NIfTI output> can be single file or directory" << endl;
	exit(0);
}

//****************************************************************************
//
// Purpose: 
//   
// Parameters: name of input file(s) or input directory
//             last parameter name of output directory (create if needed)
//   
// Returns: 
//   
// Notes: 
//
//****************************************************************************

int main(int argc, char **argv)
{
	if (argc < 3)
	{
		if ( (argc > 1) && ((strcmp(argv[1], "--version") == 0) || (strcmp(argv[1], "-V") == 0)) )
		{
			cout << "dinifti " << VERSION << endl;
			exit(0);
		}

		ShowHelp(argv);
	}
	
	// Last parameter is output file, get that out of the way

	int outNameIndex = --argc;

	DiniftiOptions opts;
	
	int parseArg = 1;
	while (parseArg < argc)
	{
		if (argv[parseArg][0] != '-')
			break;
		
		if (! strcmp(argv[parseArg], "-f") )
		{
			++parseArg;
			if (! strcmp(argv[parseArg], "a2") )
				opts.niftiFormat = ANALYZE;
			else if (! strcmp(argv[parseArg], "n1") )
				opts.niftiFormat = NIfTI_SINGLE;
			else if (! strcmp(argv[parseArg], "n2") )
				opts.niftiFormat = NIfTI_DUAL;
			else
			{
				cerr << "\t**** Error: format " << argv[2] << " not supported." << endl << endl;
				ShowHelp(argv);
			}
		
		}
		else if (! strcmp(argv[parseArg], "-g") )
		{
			opts.compressed = true;
		}
		else if (! strcmp(argv[parseArg], "-d") )
		{
			opts.useSerDesc = true;
		}
		else if (! strncmp(argv[parseArg], "--name=", 7) )
		{
			opts.nameFormat = new char [strlen(argv[parseArg]) - 6];
			strcpy(opts.nameFormat, &(argv[parseArg][7]));
		}
		else if ( (strcmp(argv[parseArg], "-v") == 0) || (strcmp(argv[parseArg], "--verbose") == 0) )
		{
			opts.verbose = true;
		}
		else if ( (strcmp(argv[parseArg], "-n") == 0) || (strcmp(argv[parseArg], "--noact") == 0) )
		{
			opts.noact = true;
		}
		else if (! strcmp(argv[parseArg], "-s") )
		{
			++parseArg;
			opts.numSlices = atoi(argv[parseArg]);
		}
		else
			ShowHelp(argv);

		++parseArg;
	}

	if (parseArg == argc)
		ShowHelp(argv);
	
	vector<string> dicomFiles;
	
	// If only two file parameters, it might be a directory (try opening it)

	DIR *dirStr = NULL; 
	
	if ((argc == parseArg + 1) && ( dirStr = opendir(argv[parseArg])))
	{
		// If this is a directory, find all DICOM files there

		dirent *nextFile = NULL;
		while (nextFile = readdir(dirStr))
		{
			// Avoid hidden files
			if (nextFile->d_name[0] != '.')
				dicomFiles.push_back(string(argv[parseArg]) + string("/") + string(nextFile->d_name));
		}
	}
	else
	{
		while (--argc >= parseArg)
			dicomFiles.push_back(string(argv[argc]));
	}

	// Try to open DICOM file(s) and process one at a time and store in lists
	// then sort according to anatomical position

	// We create separate lists for each time point
	// If this is mosaic, each list will only contain one entry
	// The key is the acquisition number

	SERIESMAP seriesMap;

	StoreImageFiles(dicomFiles, seriesMap, argv[outNameIndex], opts);	

	// Turn off excessive nifti messages

	nifti_set_debug_level(0);
	
	// Process each series, one at a time

	for (SERIESMAP::iterator series = seriesMap.begin(); series != seriesMap.end(); series++)
		ProcessSeries(series->second, opts);

	exit(0);
}

//****************************************************************************
//
// Purpose: Create the series map from all the file's we've read
//   
// Parameters: vector<string> &dicomFiles - the list
//             SERIESMAP &seriesMap       - where to store the map
//             char *outName              - where it goes
//
//             in DiniftiOptions class:
//             bool useSerDesc            - append series description to name ?
//             char nameFormat            - specified special format for name ?
//             int numSlices              - number of slices per volume
//   
// Returns: NONE
//   
// Notes: Each series is identified by their series ID, but in special cases
//        (anatomical images), the same ID is assigned to images that
//        effectively belong to separate series. In this case an equal number of
//        images is assigned to each group and we create new virtual series with
//        the series ID appended with the group number.
//
//        If numSlices not equal 0, use it to compute current acquisition number.
//
//****************************************************************************

void StoreImageFiles(vector<string> &dicomFiles, SERIESMAP &seriesMap, char *outName,
					 const DiniftiOptions& opts)
{
	// Figure out if the outName is a directory or the name of a file

	const bool outDirectory = (opendir(outName) != NULL);

	// Make sure outname doesn't already have extension, or we end up with
	// "double" extension (e.g., ".nii.nii"), which FSL doesn't like

	if ( !outDirectory )
	{
		char *extPos = strrchr(outName, '.');
		if ( extPos != NULL )
			if ( (strcmp(extPos, ".nii") == 0) || (strcmp(extPos, ".nia") == 0) || (strcmp(extPos, ".hdr") == 0) )
				*extPos = '\0';
	}
	
	int relSeriesNumber = 0;

	if (opts.verbose)
	{
		cout << "Reading " << dicomFiles.size() << " DICOM files." << endl;
	}

	while (dicomFiles.size() > 0)
	{
		string dcmFile(dicomFiles.back());
		dicomFiles.pop_back();

		DICOMImage *dcmImg = new DICOMImage(dcmFile.c_str(), opts.verbose, (opts.numSlices > 0));
		if (*dcmImg)
		{
			// If no entry for this list exists yet, create it
			SeriesInfo si = seriesMap[dcmImg->SeriesID()];

			int imgNum = dcmImg->ImageNumber();

			// The qcquisition number can be provided by the user or
			// If the number of slices greater than 1, use them to compute it.
			// Else, if the echo number is greater than 0 use that (+1)
			// Finally it can come directly from the header
			
			int acqNum = 0;
			if ( opts.numSlices > 0 )
			{
				acqNum = ((imgNum - 1) / opts.numSlices) + 1;
			}
			else if ( !dcmImg->Mosaic() && (dcmImg->NumSlices() > 1) )
			{
				acqNum = ((imgNum - 1) / dcmImg->NumSlices()) + 1;
			}
			else if ( !dcmImg->Mosaic() && (dcmImg->ACQEchoNumber() > 0) )
			{
				acqNum = dcmImg->ACQEchoNumber() + 1;
			}
			else
				acqNum = dcmImg->ACQNumber();

			// Try to determine if the acquisition number makes sense

			IMAGELIST *timePoint = si.imageMap[acqNum];
			if (timePoint == NULL)
			{
				si.imageMap[acqNum] = timePoint = new IMAGELIST;

				// If using series description, use that as the output name
				// else if outName is not a directory and more than one image,
				// create unique output name
				
				si.outName = outName;
				if (outDirectory)
				{
					if ( (opts.nameFormat != NULL) && (strlen(opts.nameFormat) > 0) )
					{
						si.outName += string("/");
						FormattedName(dcmImg, opts.nameFormat, si.outName);
					}
					else if ( opts.useSerDesc )
					{
						si.outName += string("/") + dcmImg->SeriesDescription();
					}
					else
					{
						if (outName[strlen(outName)-1] != '/')
							si.outName += string("/");
						size_type begName = dcmImg->ImagePath().find_last_of('/');
						size_type endName = dcmImg->ImagePath().find_last_of('.');

						// If the extension is a number, it probably means something

						const int nameLen = dcmImg->ImagePath().length();
						for (int extC = endName+1; extC < nameLen; ++extC)
						{
							char c = dcmImg->ImagePath()[extC];
							if (! isdigit(c) ) break;
							if (extC == nameLen-1) endName = nameLen;
						}
						endName -= begName;
						si.outName += dcmImg->ImagePath().substr(++begName, --endName);
					}
				}
				else if ( (opts.nameFormat != NULL) && (strlen(opts.nameFormat) > 0) )
				{
					si.outName += string("+");
					FormattedName(dcmImg, opts.nameFormat, si.outName);
				}
				else if ( opts.useSerDesc )
				{
					si.outName += string("+") + dcmImg->SeriesDescription();
				}
				else if ( seriesMap.size() > 1 )
				{
					ostringstream newFileName(si.outName, ostringstream::app);
					newFileName << "+" << ++relSeriesNumber;
					si.outName = newFileName.str();
				}
			}
			
			timePoint->push_back(*dcmImg);
			
			si.mosaic = dcmImg->Mosaic();
			si.numSlices = dcmImg->NumSlices();
			si.multiEcho = (dcmImg->ACQEchoNumber() > 0);
			
			if (imgNum > si.maxImgNum)
				si.maxImgNum = imgNum;
			if ( (imgNum < si.minImgNum) || (si.minImgNum == 0) )
				si.minImgNum = imgNum;
			if (acqNum > si.maxAcqNum)
				si.maxAcqNum = acqNum;
			if ( (acqNum < si.minAcqNum) || (si.minAcqNum == 0) )
				si.minAcqNum = acqNum;
			seriesMap[dcmImg->SeriesID()] = si;
		}
		else
			delete dcmImg;
	}

	SERIESMAP newSeriesMap;
	
	// Go through the series and see if any are a special case

	// use this list to store series outNames to detect possible duplicates
	list<string> series_ids;

	// do not increment the iterator here as this has to be handled conditionally at the end
	for (SERIESMAP::iterator series = seriesMap.begin(); series != seriesMap.end(); )
	{
		SeriesInfo si = series->second;

		// Loop through images in each list and verify if number of images is
		// consistent, otherwise we need to split them in more lists

		std::map<int, bool> fixMap;
		
		for ( IMAGEMAP::iterator timeIter = si.imageMap.begin(); timeIter != si.imageMap.end(); ++timeIter )
		{
			if ( ! si.mosaic )
			{
				int acqNum = timeIter->first;
				IMAGELIST *timePointList = timeIter->second;
				fixMap[acqNum] = timePointList->size() != timePointList->front().NumSlices();
			}
		}

		for ( std::map<int, bool>::iterator fixIter = fixMap.begin(); fixIter != fixMap.end(); ++fixIter )
		{
			if ( fixIter->second )
			{
				// We need to fix this list. Use echo number to augment list size.

				IMAGELIST *timePointList = si.imageMap[fixIter->first];
				IMAGEMAP splitMap;
				si.minAcqNum = si.maxAcqNum;
				
				for ( IMAGELIST::iterator timeIter = timePointList->begin(); timeIter != timePointList->end(); ++timeIter)
				{
					int thisAcqNum = timeIter->ACQEchoNumber();
					IMAGELIST *timePoint = splitMap[thisAcqNum];
					if ( timePoint == NULL )
					{
						splitMap[thisAcqNum] = timePoint = new IMAGELIST;
						if ( thisAcqNum > si.maxAcqNum ) si.maxAcqNum = thisAcqNum;
						if ( thisAcqNum < si.minAcqNum ) si.minAcqNum = thisAcqNum;
					}
					timePoint->push_back(*timeIter);
				}
				si.imageMap.swap(splitMap);
				series->second = si;
			}
		}
		
		// For each series, check the number of groups (first image in first time point is sufficient)

		IMAGEMAP fullImageMap = si.imageMap;
		IMAGEMAP::iterator fimIter = fullImageMap.begin();
		IMAGELIST *firstTimePoint = fimIter->second;
		DICOMImage firstImg = firstTimePoint->front();
		const int numGroups = firstImg.NumGroups();

		// Try to guess if everything's allright with number of time points
		if (opts.verbose)
		{
			cout << "Found new image series. Using output destination '" << si.outName << "'." << endl;
		}

		// check for duplicate series outNames
		int dupes = count( series_ids.begin(), series_ids.end(), si.outName );

		// if duplicate series name
		if (dupes)
		{
			// store the old for a useful warning message
			string old = si.outName;

			// appen '+' until we have a unique name
			while (dupes)
			{
				si.outName.append("+");
				dupes = count( series_ids.begin(), series_ids.end(), si.outName );
			}
			// issue warning to notify the name change
			cerr << endl << "\t**** WARNING: Duplicate image series name '" <<
				old << "'" << endl
				<< "\t**** New image series name is '" << si.outName << "'." << endl;

			// important: reassign the modified series info (lost otherwise)
			series->second = si;
		}

		// store the current image series name
		series_ids.push_back(si.outName);

		if ( (opts.numSlices == 0) &&
			 (si.numSlices == 1) &&
			 (si.maxAcqNum == si.minAcqNum) &&
			 (si.maxImgNum > si.minImgNum) )
		{
			cerr << endl << "\t**** StoreImageFiles: Warning: number of slices per volume = 1" << endl
				 << "\t**** and number of time points = 1." << endl
				 << "\t**** If incorrect, run program with \"-s\" option." << endl;
		}

		const int numAcq = (si.multiEcho ? 1 : (si.maxAcqNum - si.minAcqNum + 1));
		int numImgGroup = (si.maxImgNum - si.minImgNum + 1) / (numGroups * numAcq);

		if ( (numImgGroup > 0) && (numGroups > 1) )
		{
			// We need to create "virtual" series and split the images among them

			string origSeriesID = series->first;

            // Hold the names in a map for easy retrieval

			map<int, string> newSeriesNames;
			for (int group = 1; group <= numGroups; ++group)
			{
				ostringstream newName;
				newName << origSeriesID << "-" << group;
				newSeriesNames[group] = newName.str();
			}
			
			for (IMAGEMAP::iterator timeIter = fullImageMap.begin(); timeIter != fullImageMap.end(); ++timeIter)
			{
				// Grab the full list of images for this time point
				// And split them up among the groups

				int acqNum = timeIter->first;
				IMAGELIST *timePoint = timeIter->second;
				for (IMAGELIST::iterator img = timePoint->begin(); img != timePoint->end(); ++img)
				{
					// If we have info from Siemen's shadow set, use it, otherwise
					// we know the acqisition number, figure out which group it belongs to.

					int group = img->GroupNumber(img->ImageNumber()) + 1;
					if ( (group == 1) && (img->GroupSize(0) == 0) )
						group = (img->ImageNumber() - si.minImgNum) / numImgGroup + 1;
					SeriesInfo newSeriesInfo = newSeriesMap[newSeriesNames[group]];
					IMAGELIST *newTimePoint = newSeriesInfo.imageMap[acqNum];
					if (newTimePoint == NULL)
					{
						newSeriesInfo.imageMap[acqNum] = newTimePoint = new IMAGELIST;
						ostringstream newName;
						newName << si.outName << "-" << group;
						newSeriesInfo.outName = newName.str();
						newSeriesInfo.mosaic = si.mosaic;

						// If we have Siemen's info from Siemen's showdow set, use it

						if (img->GroupSize(0) > 0 )
						{
							newSeriesInfo.numSlices = img->GroupSize(group -1);
							newSeriesInfo.minImgNum = img->GroupStartImage(group-1);
							newSeriesInfo.maxImgNum = newSeriesInfo.minImgNum + newSeriesInfo.numSlices - 1;
						}
						else
						{
							newSeriesInfo.minImgNum = (group - 1) * numImgGroup + si.minImgNum;
							newSeriesInfo.maxImgNum = group * numImgGroup + si.minImgNum - 1;
						}
						
						newSeriesInfo.maxAcqNum = si.maxAcqNum;
						newSeriesInfo.minAcqNum = si.minAcqNum;
					}
					
					newTimePoint->push_back(*img);
					newSeriesMap[newSeriesNames[group]] = newSeriesInfo;
				}
			}

			// Now we can get rid of the old entries

			// FIRST increment the iterator THEN erase the entry
			// otherwise the iterator becomes invalid
			seriesMap.erase(series++);
		}
		else
			// if not incremented above do here
			++series;
	}

	// Move the added series to permanent storage

	seriesMap.insert(newSeriesMap.begin(), newSeriesMap.end());
	
	// If not mosaic sort and do some post-processing

	for (SERIESMAP::iterator series = seriesMap.begin(); series != seriesMap.end(); series++)
	{
		SeriesInfo si = series->second;
		if (!si.mosaic)
		{			
			const int numAcq = (si.multiEcho ? 1 : (si.maxAcqNum - si.minAcqNum + 1));
			const int numSlices = (opts.numSlices > 0 ? opts.numSlices :
								   (si.numSlices > 1 ? si.numSlices : (si.maxImgNum - si.minImgNum + 1) / numAcq));
			
			for (IMAGEMAP::iterator timeIter = si.imageMap.begin(); timeIter != si.imageMap.end(); ++timeIter)
			{
				IMAGELIST *timePoint = timeIter->second;
				timePoint->sort(CompareImages);
				for (IMAGELIST::iterator image = timePoint->begin(); image != timePoint->end(); image++)
					image->AdjustSet(numSlices, si.minImgNum-1);
			}
		}
	}
	
}

//****************************************************************************
//
// Purpose: Process a series
//   
// Parameters: SeriesInfo &seriesInfo  - the series
//
//             in DiniftiOptions class:
//             OUTPUT_TYPE niftiFormat - how the output is written
//             bool compressed         - compressed output format
//   
// Returns: 
//   
// Notes: The name will be taken from the first file in the series
//
//****************************************************************************

void ProcessSeries(SeriesInfo &seriesInfo, const DiniftiOptions& opts)
{
	// Create NIfTI header with default values
	
	if (opts.verbose)
	{
		cout << "Processing image series ('" << seriesInfo.outName << "')." << endl;
	}

	nifti_image niftiHdr;
	if (! NIfTICreateHeader(niftiHdr, seriesInfo.imageMap))
		return;
	niftiHdr.nifti_type = (int)opts.niftiFormat;

	// main processing loop over the repeat counters

	SetFileName(niftiHdr.fname, seriesInfo.outName.c_str(), opts.niftiFormat, opts.compressed);

	// In case of two files, set the name here before nifti muck about with it
	
	if ( (opts.niftiFormat == ANALYZE) || (opts.niftiFormat == NIfTI_DUAL) )
		SetFileName(niftiHdr.iname, seriesInfo.outName.c_str(), opts.niftiFormat, opts.compressed, false);

	if (!opts.noact)
	{
		znzFile outfp = NULL;
		outfp = nifti_image_write_hdr_img(&niftiHdr, 2, "wb");
		
		if (outfp == NULL)
		{
			cerr << "Writing output header failed for " << niftiHdr.fname
				 << ". Continuing to next series." << endl << endl;
		}
		else
		{
			NIfTIWriteData(outfp, niftiHdr, seriesInfo.imageMap);
			znzclose(outfp);
		}
	}

	// Clean up time

	delete [] niftiHdr.fname;
	if (niftiHdr.iname != NULL) delete [] niftiHdr.iname;
}
