content: package edu.stanford.rsl.conrad.volume3d;
  
  
  import ij.ImagePlus;
  import ij.ImageStack;
  import edu.stanford.rsl.conrad.utils.ImageUtil;
  
  
  /**
   * 3D-FFTable Volume based on C-code from Lars Wigstroem. This volume class is able to describe a 3D grid with uniform spacing in each spatial dimension. One sub class of this volume is CUDAVolume3D which models the same data in CUDA memory. 
   * The corresponding operator is then CUDAVolumeOperator. Note that instantiation should be performed using the respective VolumeOperator in order to enable compatibility to CUDA.
   * 
   * @author akmaier
   * @see edu.stanford.rsl.conrad.cuda.CUDAVolume3D
   * @see edu.stanford.rsl.conrad.volume3d.VolumeOperator
   */
  public class Volume3D {
  
  	public int     dimensions;        /* #dimensions               */
  	public int[]   size  = new int [MAX_DIM];     /* #points in each dimension */
  	public float[] spacing = new float [MAX_DIM];      /* size of points [m, s, ..] */
  	public int   in_dim;            /* inner dimension, i.e. complex or real  */ 
  	public float [] [] [] data;        /* buffer holding the data   */
  	/* points at first element   */
  
  	public final static int MAX_DIM = 3;
  	protected final static boolean DEBUG_FLAG = true;
  
  /**
   * returns 1 if the volume is real and 2 if it is complex
   * @return the internal dimension of the volume.
   */
  	public int getInternalDimension(){
  		return in_dim;
  	}
  	
  	/**
  	 * Creates an empty volume. Use of this constructor is generally discouraged. It may result in incompatibility with CUDA.
  	 * @param size2 sizes in each direction
  	 * @param dim2 physical dimension in each direction
  	 * @param inDim internal Dimension
  	 */
  	public Volume3D(int[] size2, float[] dim2, int inDim) {
  		/* set attributes */
  
  		this.dimensions = 3;
  
  		for (int dim_loop=0; dim_loop<this.dimensions; dim_loop++) {
  			this.size[dim_loop] = size2[dim_loop];
  			this.spacing[dim_loop]  = dim2[dim_loop];
  		}
  		this.in_dim = inDim;
  
  		/* allocate memory for data buffer */
  		data = new float [size[0]][size[1]][size[2]*in_dim];		
  
  	}
  	
  	/**
  	 * Creates a Volume3D Object which supports 3-D filtering operations. Call of this constructor is discouraged. Use the VolumeOperator instead.
  	 * 
  	 * @param image the ImagePlus
  	 * @param mirror size of the area which is mirrored to reduce FFT artifacts.
  	 * @param cuty number of pixels to be cut from the original volume along y direction
  	 * @param uneven is set if the original volume has an uneven number of slices / projections.
  	 */
  	public Volume3D(ImagePlus image, int mirror, int cuty, boolean uneven){
  		dimensions = 3;
  		in_dim = 1;
  		spacing = new float [3];
  		spacing[2] = (float) image.getCalibration().pixelWidth;
  		spacing[1] = (float) image.getCalibration().pixelHeight;
  		spacing[0] = (float) image.getCalibration().pixelDepth;
  		size = new int[3];
  		int width = image.getWidth();
  		int height = image.getHeight();
  		int depth = image.getStackSize();
  		size[2]=image.getWidth()+(2*mirror);
  		size[1]=image.getHeight()+(2*mirror)-(2*cuty);
  		if (!uneven){
  			size[0]=image.getStackSize()+(2*mirror);
  		} else {
  			size[0]=image.getStackSize()+(2*mirror)-1;
  		}
  		data = new float[size[0]][size[1]][size[2]];
  		for (int h = 0; h< depth; h++){
  			for (int j = cuty; j< height - cuty; j++){
  				for (int i = 0; i< width; i++){
  					data[mirror + h][mirror +j-cuty][mirror +i] = ((float [])image.getStack().getPixels(h+1))[(width*j)+i];
  				}
  				for (int i = 0; i< mirror; i++){
  					data[mirror + h][mirror +j-cuty][i] = ((float [])image.getStack().getPixels(h+1))[(width*j)+(mirror)-i-1];
  					data[mirror + h][mirror +j-cuty][width + mirror + i] = ((float [])image.getStack().getPixels(h+1))[(width*j)+width-i];
  				}
  			}
  			for (int j = 0; j< mirror; j++){
  				for (int i = 0; i < size[2]; i++){
  					data[mirror + h][j][i] = data[mirror + h][(2*mirror)-j-1][i];
  					data[mirror + h][size[1]-mirror+j][i] = data[mirror+h][size[1]-(mirror)-j-1][i];
  				}
  			}
  		}
  		int offset = 0;
  		if (uneven) offset = 1;
  		for (int h=0;h< mirror; h++){
  			for(int j = 0; j < size[1]; j++){
  				for (int i = 0; i < size[2]; i++){
  					data[h][j][i] = data[(2*mirror)-h-1][j][i];
  					if (size[0]-mirror+h-offset < size[0]) {
  						data[size[0]-mirror+h-offset][j][i] = data[size[0]-mirror-h-1-offset][j][i];
  					}
  				}
  			}
  		}
  		//this.getImagePlus("Mirrored").show();
  	}
  	
  	/**
  	 * Creates an ImagePlus to visualize the contents of this Volume.
  	 * @param title the title of the ImagePlus
  	 * @return the ImagePlus
  	 */
  	public ImagePlus getImagePlus(String title){
  		return getImagePlus(title, 0, 0, false);
  	}
  	
  	/**
  	 * Method to create an ImagePlus from this Volume3D. Parameters are set to remove the mirrored boundary.
  	 * @param title the title for the ImagePlus
  	 * @param mirror the width of the mirrored boundary. Should match the parameters used to create the volume.
  	 * @param cuty the number of pixels which were cut along the y axis during the creation of the volume.
  	 * @param uneven true, if the original number of slices / projections was odd.
  	 * @return the ImagePlus
  	 */
  	public ImagePlus getImagePlus(String title, int mirror, int cuty, boolean uneven){
  		ImagePlus revan = new ImagePlus();
  		int width = size[2]-(2*mirror);
  		int height = size[1]-(2*mirror)+(2*cuty);
  		int depth;
  		int offset;
  		if (uneven) {
  			depth = size[0]-(2*mirror)+1;
  			offset = 1;
  		} else {
  			depth = size[0]-(2*mirror);
  			offset = 0;
  		}
  		ImageStack stack = new ImageStack(width, height, depth);
  		System.out.println("Image Plus with internal Dimension: " + in_dim);
  		for (int h = mirror; h < size[0]-mirror+offset; h++){
  			float [] pixels = new float[width*height];
  			for (int j = mirror-cuty; j< size[1]-mirror+cuty; j++){
  				for (int i = mirror; i< size[2]-mirror; i++){
  					pixels[(j-mirror+cuty)*(width)+(i-mirror)] = 
  						data[h][j][i*in_dim];
  				}
  			}
  			stack.setPixels(pixels, h-mirror+1);
  		}
  		stack.setColorModel(ImageUtil.getDefaultColorModel());
  		revan.setStack(title, stack);
  		return revan;
  	}
  
  
  
  
  	/**
  	 * prints the dimensions of the volume to STDOUT.
  	 */
  	public void printSize(){
  		for (int i =0; i < dimensions; i++) {
  			System.out.print(size[i]+ " ");
  		}
  		System.out.println("Inner Dimension: " + in_dim);
  		if(DEBUG_FLAG) {
  			System.out.println(data.length + " " + data[0].length + " " + data[0][0].length);
  		}
  	}
  	
  
  	/**
  	 * Releases the memory for this volume. References to data are set to null to improve garbage collection. In the CUDA version, the memory on the card is freed.
  	 */
  	public void destroy(){
  		spacing = null;
  		size = null;
  		data = null;
  	}
  	
  }
  /*
   * Copyright (C) 2010-2014  Andreas Maier
   * CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
  */