image_stack.h 6.83 KB

#include <stim/parser/wildcards.h>
#include <stim/parser/filename.h>
#include <stim/grids/grid.h>
#include <stim/image/image.h>
#include <stim/math/vec3.h>

namespace stim{

///This class is used to load 3D grid data from stacks of images
//	The class uses a 4D grid object, where the first dimension is a color value.
template<typename T, typename F = float>
class image_stack : public virtual stim::grid<T, 4, F>{

	enum image_type {stimAuto, stimMono, stimRGB, stimRGBA};

	//using stim::grid<T, 4>::S;
	using stim::grid<T, 4, F>::R;
	using stim::grid<T, 4, F>::ptr;
	using stim::grid<T, 4, F>::S;

	//default constructor
	image_stack() : grid<T, 4, F>() {


	/// Overloads grid::samples() to return the number of samples associated with a given spatial dimension
	///		this is necessary because R[0] stores the color
	size_t samples(size_t d){
		return grid<T, 4, F>::samples(d + 1);

	size_t samples(){
		return R[1] * R[2] * R[3];						//return the number of spatial samples

	// get the number of pixels in each dimension
	size_t nc() {
		return R[0];
	size_t nx() {
		return R[1];
	size_t ny() {
		return R[2];
	size_t nz() {
		return R[3];

	/// Returns the number of color channels
	size_t channels(){
		return R[0];

	/// Overloads grid::size() to return the size of the grid associated with a given spatial dimension
	F size(size_t d){
		return grid<T, 4, F>::size(d + 1);

	/// Sets the spacing between samples in the image stack
	void spacing(F sx, F sy, F sz){
		grid<T, 4, F>::S[1] = sx;			//set the sample spacing for the appropriate spatial dimension
		grid<T, 4, F>::S[2] = sy;
		grid<T, 4, F>::S[3] = sz;

	F spacing(size_t d){
		return grid<T, 4, F>::spacing(d + 1);

	/// Overloads the spacing parameter to set the size of the grid associated with a given spatial dimension
	//void spacing(F sx, F sy = 1.0f, F sz = 1.0f){
	//	grid<T, 4, F>::spacing((F)1.0, sx, sy, sz);

	/// Load all of the images specified by a list of strings
	/// @param string_list is a list of file names specifying images
	void load_images(std::vector<std::string> string_list){

		//if there are no matching files, exit
		if(string_list.size() == 0){
			std::cout<<"STIM ERROR (image_stack): No matching files for loading a stack."<<std::endl;

		stim::image<T> I(string_list[0]);		//load the first image and set all of the image_stack proparties

		R[0] = I.channels();				//set the number of color channels
		R[1] = I.width();				//set the stack height and width based on the image size
		R[2] = I.height();
		R[3] = string_list.size();			//set the stack z-resolution based on the number of images

		ptr = (T*)malloc(grid<T, 4, F>::bytes());	//allocate storage space

		//load and copy each image into the grid
		for(unsigned int i = 0; i<R[3]; i++){				//for each image in the list			
			stim::image<T> I(string_list[i]);			//load the image			
			I.get_interleaved_rgb(&ptr[ i * R[0] * R[1] * R[2] ]);	//retrieve the interlaced data from the image - store it in the grid			

	/// Load a stack of images based on a file mask. Images are loaded in alphanumeric order
	/// @param file_mask is the mask describing the images to be loaded
	void load_images(std::string file_mask){
		stim::filename file_path(file_mask);				//get the path for the images
		std::vector<stim::filename> file_list = file_path.get_list();	//get the list of files
		std::vector<std::string> string_list(file_list.size());		//allocate space for an array of strings
		for(size_t f = 0; f < file_list.size(); f++){			//for each file name in the list
			string_list[f] = file_list[f].str();			//convert the file name to a string
		load_images(string_list);					//load all of the images in the list

	///Inserts image I into slot i.
	/// @param stim::image<T> I; image to insert.
	/// @int I, where to place the image.
	void insert_image(stim::image<T> I, int i){
		I.get_interleaved_rgb(&ptr[i *R[0] *R[1] *R[2] ]);

	///Saves a single page to an image file
	/// @param file_name is the name of the image file to be created
	/// @param i is the page to be saved
	void save_image(std::string file_name, unsigned int i){		
		stim::image<T> I;											//create an image		
		I.set_interleaved(&ptr[ i * R[0] * R[1] * R[2] ], R[1], R[2], R[0]);	//retrieve the interlaced data from the image - store it in the grid
//		I.set_interleaved_rgb(&ptr[ i * R[0] * R[1] * R[2] ], R[1], R[2], R[0]);	//retrieve the interlaced data from the image - store it in the grid;

	///Sets the dimensions of the image in each direction
	/// @param x size in the x direction
	/// @param y size in the y direction
	/// @param z size in the z direction
	set_dim(float x, float y, float z)
		grid<T, 4, F>::S[0] = 1;
		grid<T, 4, F>::S[1] = x;
		grid<T, 4, F>::S[2] = y;
		grid<T, 4, F>::S[3] = z;

	///set dimensions of the grid.
	/// @param channels, number of channels in each image.
	/// @param width,  number of pixels in width each image.
	/// @param height, number of pixels in height.
	/// @param depth,  number of pixels in depth.
	void init(int channels, int width, int height, int depth)
		R[0] = channels;
		R[1] = width;
		R[2] = height;
		R[3] = depth;

		ptr = (T*)malloc(sizeof(T) * samples());
		memset(ptr, 0, sizeof(T) * samples());

	///Saves the entire stack to a set of images
	/// @param file_mask is the mask describing how the file names will be saved (ex. image????.bmp)
	void save_images(std::string file_mask){

		stim::filename file_path(file_mask);
		//stim::filename abs_file_path = file_pat

		//create a list of file names
		std::vector<std::string> file_list = stim::wildcards::increment(file_path.str(), 0, R[3]-1, 1);
		for (int i = 0; i < R[3]; i++) {
			save_image(file_list[i], i);

	/// Returns the pixel at the specified point
	T get(unsigned int x, unsigned int y, unsigned int z, unsigned int c = 0){
		return ptr[z * R[0] * R[1] * R[2] + y * R[0] * R[1] + x * R[0] + c];

	/// Returns the world-space position at an index point (i, j, k)
	vec3<F> p(size_t i, size_t j, size_t k){
		vec3<F> result;
		result[0] = (F)i * S[1];
		result[1] = (F)j * S[2];
		result[2] = (F)k * S[3];
		return result;

	// set the pixel at the specified point
	void set(size_t i, size_t j, size_t k, T value, size_t c = 0){
		ptr[k * R[0] * R[1] * R[2] + j * R[0] * R[1] + i * R[0] + c] = value;
	void set(T* Ptr, size_t k) {
		for (unsigned i = 0; i < R[0] * R[1] * R[2]; i++)
			ptr[i + k * R[0] * R[1] * R[2]] = Ptr[i];
	void copy(T* Ptr) {
		ptr = Ptr;

	/* This was causing compiler errors. I don't think this function call exists anywhere:

	void read(std::string file, unsigned int X, unsigned int Y, unsigned int Z, unsigned int C = 1, unsigned int header = 0){
		read(file, stim::vec<unsigned long>(C, X, Y, Z), header);

	T* data(){
		return ptr;