binary.h 6.72 KB

//make sure that this header file is only loaded once
#ifndef RTS_BINARY_H
#define RTS_BINARY_H

#include "../envi/envi_header.h"
#include "../math/vector.h"
#include <fstream>
#include <sys/stat.h>

namespace stim{

/** This class manages the streaming of large multidimensional binary files.
 *  Generally these are hyperspectral files with 2 spatial and 1 spectral dimension. However, this class supports
 *  other dimensions via the template parameter D.
 *
 *  @param T is the data type used to store data to disk (generally float or double)
 *  @param D is the dimension of the data (default 3)
 */
template< typename T, unsigned int D = 3 >
class binary{

protected:
	std::fstream file;		//file stream used for reading and writing
	std::string name;		//file name

	unsigned long long int R[D];		//resolution
	unsigned int header;	//header size (in bytes)
	unsigned char* mask;	//pointer to a character array: 0 = background, 1 = foreground (or valid data)




	/// Private initialization function used to set default parameters in the data structure.
	void init(){
		memset(R, 0, sizeof(unsigned int) * D);		//initialize the resolution to zero
		header = 0;									//initialize the header size to zero
		mask = NULL;
	}

	/// Private helper function that returns the size of the file on disk using system functions.
	long long int get_file_size(){
#ifdef _WIN32
		struct _stat64 results;
		if(_stat64(name.c_str(), &results) == 0)
			return results.st_size;
#else
		struct stat results;
		if(stat(name.c_str(), &results) == 0)
			return results.st_size;
#endif
		else return 0;
	}

	/// Private helper function that tests to make sure that the calculated data size specified by the structure is the same as the data size on disk.
	bool test_file_size(){
		long long int npts = 1;				//initialize the number of data points to 1
		for(unsigned int i = 0; i<D; i++)	//iterate over each dimension
			npts *= R[i];					//compute the total number of data points in the file
		long long int datasize = npts * sizeof(T);//multiply the sum by the size of the template parameter

		if(datasize + header == get_file_size()) return true;	//if the byte size matches the file size, we're golden
		else return false;					//otherwise return an error

	}

	/// Private helper file that opens a specified binary file.

	/// @param filename is the name of the binary file to stream
	bool open_file(std::string filename){
		//open the file as binary for reading and writing
		file.open(filename.c_str(), std::ios::in | std::ios::out | std::ios::binary);

		//if the file is successful
		if(file){
			name = filename;		//set the name
			if(test_file_size())	//test the file size
				return true;
		}
		
		return false;
	}

public:

	/// Open a binary file for streaming.

	/// @param filename is the name of the binary file
	/// @param r is a STIM vector specifying the size of the binary file along each dimension
	/// @param h is the length (in bytes) of any header file (default zero)
	bool open(std::string filename, vec<unsigned int, D> r, unsigned int h = 0){

		for(unsigned int i = 0; i < D; i++)		//set the dimensions of the binary file object
			R[i] = r[i];

		header = h;				//save the header size

		if(!open_file(filename)) return false;	//open the binary file

		return test_file_size();
	}

	/// Creates a new binary file for streaming

	/// @param filename is the name of the binary file to be created
	/// @param r is a STIM vector specifying the size of the file along each dimension
	/// @offset specifies how many bytes to offset the file (used to leave room for a header)
	bool create(std::string filename, vec<unsigned int, D> r, unsigned int offset = 0){

		std::ofstream target(filename.c_str(), std::ios::binary);

		//initialize binary file
		T p = 0;
		for(unsigned int i =0; i < r[0] * r[1] * r[2]; i++){
			target.write((char*)(&p), sizeof(T));
		}

		for(unsigned int i = 0; i < D; i++)		//set the dimensions of the binary file object
			R[i] = r[i];

		header = offset;				//save the header size

		if(!open_file(filename)) return false;	//open the binary file

		return test_file_size();
	}

	/// Writes a single page of data to disk. A page consists of a sequence of data of size R[0] * R[1] * ... * R[D-1].

	/// @param p is a pointer to the data to be written
	/// @param page is the page number (index of the highest-numbered dimension)
	bool write_page( T * p, unsigned int page){

		if(p == NULL){
			std::cout<<"ERROR: unable to write into file, empty pointer"<<std::endl;
			exit(1);
		}

		file.seekg(R[1] * R[0] * page * sizeof(T), std::ios::beg);   //seek to the desired location on disk
		file.write((char *)p, R[0] * R[1] * sizeof(T));				 //write binary data

		return true;
	}

	/// Reads a page from disk. A page consists of a sequence of data of size R[0] * R[1] * ... * R[D-1].

	/// @param p is a pointer to pre-allocated memory equal to the page size
	/// @param page is the index of the page
	bool read_page( T * p, unsigned int page){

		if (page >= R[2]){										//make sure the bank number is right
			std::cout<<"ERROR: page out of range"<<std::endl;
			return false;
		}

		file.seekg(R[1] * R[0] * page * sizeof(T), std::ios::beg);   //write into memory from the binary file
		file.read((char *)p, R[0] * R[1] * sizeof(T));

		return true;
	}

	//saves a hyperplane orthogonal to dimension d at intersection n
	bool read_plane(T * dest, unsigned int d, unsigned int n){

		//reset the file pointer back to the beginning of the file
		file.seekg(0, std::ios::beg);

		//compute the contiguous size C for each readable block
		unsigned int C = 1;
		for(unsigned int i = 0; i < d; i++)		//for each dimension less than d
			C *= R[i];							//compute the product

		//compute the non-contiguous size NC for each readable block
		unsigned int NC = 1;
		for(unsigned int i = d + 1; i < D; i++)
			NC *= R[i];

		//for all noncontiguous blocks, read each contiguous block that makes up the hyper-plane
		for(unsigned int nc = 0; nc < NC; nc++){
			file.seekg(n * C * sizeof(T), std::ios::cur);	//skip n contiguous blocks
			file.read( (char*)&dest[nc * C], C * sizeof(T));	//read one contiguous block
			file.seekg( (R[d] - n - 1) * C * sizeof(T), std::ios::cur);	//skip R[d] - n contiguous blocks
		}

		return true;

	}

	//save one pixel of the file into the memory, and return the pointer
	bool read_spectrum(T * p, unsigned x, unsigned y){

		unsigned int i;

		if ( x >= R[0] || y >= R[1]){							//make sure the sample and line number is right
			std::cout<<"ERROR: sample or line out of range"<<std::endl;
			return false;
		}

		file.seekg((x + y * R[0]) * sizeof(T), std::ios::beg);           //point to the certain sample and line
		for (i = 0; i < R[2]; i++)
		{
			file.read((char *)(p + i), sizeof(T));
			file.seekg((R[1] * R[0] - 1) * sizeof(T), std::ios::cur);    //go to the next band
		}

		return true;	
	}


};

}

#endif