content: package edu.stanford.rsl.tutorial.RotationalAngiography.ResidualMotionCompensation.reconWithStreakReduction;
  
  
  import ij.IJ;
  import ij.ImagePlus;
  import ij.process.FloatProcessor;
  
  import java.nio.FloatBuffer;
  import java.nio.IntBuffer;
  import java.util.ArrayList;
  import java.util.Arrays;
  
  import com.jogamp.opencl.CLBuffer;
  import com.jogamp.opencl.CLCommandQueue;
  import com.jogamp.opencl.CLContext;
  import com.jogamp.opencl.CLDevice;
  import com.jogamp.opencl.CLImage2d;
  import com.jogamp.opencl.CLImageFormat;
  import com.jogamp.opencl.CLKernel;
  import com.jogamp.opencl.CLProgram;
  import com.jogamp.opencl.CLImageFormat.ChannelOrder;
  import com.jogamp.opencl.CLImageFormat.ChannelType;
  import com.jogamp.opencl.CLMemory.Mem;
  
  import edu.stanford.rsl.apps.gui.Citeable;
  import edu.stanford.rsl.conrad.data.numeric.Grid2D;
  import edu.stanford.rsl.conrad.data.numeric.NumericPointwiseOperators;
  import edu.stanford.rsl.conrad.io.ImagePlusDataSink;
  import edu.stanford.rsl.conrad.numerics.SimpleMatrix;
  import edu.stanford.rsl.conrad.opencl.OpenCLUtil;
  import edu.stanford.rsl.conrad.opencl.TestOpenCL;
  import edu.stanford.rsl.conrad.reconstruction.VOIBasedReconstructionFilter;
  import edu.stanford.rsl.conrad.utils.CONRAD;
  import edu.stanford.rsl.conrad.utils.Configuration;
  import edu.stanford.rsl.conrad.utils.ImageGridBuffer;
  import edu.stanford.rsl.conrad.utils.ImageUtil;
  import edu.stanford.rsl.conrad.utils.UserUtil;
  
  public class OpenCLBackProjectorStreakReduction extends VOIBasedReconstructionFilter implements Runnable, Citeable{
  
  	/**
  	 * 
  	 */
  	private static final long serialVersionUID = -8615490043940236889L;
  
  	private boolean forceSmallVolume = false;
  
  	static int bpBlockSize[] = {32, 16};
  
  	private static boolean debug = true;
  	/**
  	 * The OpenCL context
  	 */
  	protected CLContext context;
  
  	/**
  	 * The OpenCL program
  	 */
  	private CLProgram program;
  
  	/**
  	 * The OpenCL device
  	 */
  	private CLDevice device;
  
  	/**
  	 * The OpenCL kernel function binding
  	 */
  	private CLKernel kernelFunction;
  
  	/**
  	 * The OpenCL command queue
  	 */
  	protected CLCommandQueue commandQueue;	
  
  	/**
  	 * The 2D projection texture reference
  	 */
  	private CLImage2d<FloatBuffer> projectionTex = null;
  
  	/**
  	 * The volume data that is to be reconstructed
  	 */
  	protected float h_volume[];
  	
  	private int gat_ign = 3;
  
  
  	/**
  	 * The global variable of the module which stores the
  	 * view matrix.
  	 */
  	protected CLBuffer<FloatBuffer> projectionMatrix = null;
  	private CLBuffer<IntBuffer> volStride = null;
  	private CLBuffer<FloatBuffer> volumePointer = null;
  	private CLBuffer<FloatBuffer> projectionArray = null;
  	private CLBuffer<FloatBuffer> destBuffer = null;
  
  	protected ImageGridBuffer projections;
  	protected ArrayList<Integer> projectionsAvailable;
  	protected ArrayList<Integer> projectionsDone;
  	private boolean largeVolumeMode = false;
  	private int nSteps = 1;
  	private int subVolumeZ = 0;
  
  	private boolean initialized = false;
  
  	public OpenCLBackProjectorStreakReduction () {
  		super();
  	}
  
  	@Override
  	public void prepareForSerialization(){
  		super.prepareForSerialization();
  		projectionMatrix = null;
  		volStride = null;
  		volumePointer = null;
  		projectionArray = null;
  		projections = null;
  		projectionsAvailable =null;
  		projectionsDone = null;
  		h_volume = null;
  		initialized = false;
  
  		// JOCL members
  		program = null;
  		device = null;
  		kernelFunction = null;
  		commandQueue = null;
  		projectionTex = null;
  		context = null;
  	}
  
  	@Override
  	public void configure() throws Exception{
  		forceSmallVolume = UserUtil.queryBoolean("Force Small Volume Mode?");
  		configured = true;
  	}
  	
  	public void configure(boolean forceSmallVolumeMode) throws Exception{
  		this.forceSmallVolume = forceSmallVolumeMode;
  		configured = true;
  	}
  
  	protected void init(){
  		if (!initialized) {
  			largeVolumeMode = false;
  
  			int reconDimensionX = getGeometry().getReconDimensionX();
  			int reconDimensionY = getGeometry().getReconDimensionY();
  			int reconDimensionZ = getGeometry().getReconDimensionZ();
  			projections = new ImageGridBuffer();
  			projectionsAvailable = new ArrayList<Integer>();
  			projectionsDone = new ArrayList<Integer>();
  
  			// Initialize JOCL.
  			context = OpenCLUtil.createContext();
  
  			try {
  				// get the fastest device
  				device = context.getMaxFlopsDevice();
  				// create the command queue
  				commandQueue = device.createCommandQueue();
  
  				// initialize the program
  				if (program==null || !program.getContext().equals(this.context)){
  					program = context.createProgram(this.getClass().getResourceAsStream("backprojectCLStreakReduction.cl")).build();
  					
  					//program = context.createProgram(TestOpenCL.class.getResourceAsStream("C:\\LME\\Desktop\\Bachelorarbeit Katrin Mentl\\KONRAD\\CONRAD\\src\\edu\\stanford\\rsl\\science\\mentl\\cardiacVasculatureRecon\\reconWithScatterCorrection\\backprojectCLScatterCorr.cl")).build();
  				}
  				
  				
  
  
  			} catch (Exception e) {
  				if (commandQueue != null)
  					commandQueue.release();
  				if (kernelFunction != null)
  					kernelFunction.release();
  				if (program != null)
  					program.release();
  				// destory context
  				if (context != null)
  					context.release();
  				// TODO: handle exception
  				e.printStackTrace();
  			}
  
  			// check space on device:
  			long memory = device.getMaxMemAllocSize();
  			long availableMemory = (memory);
  			long requiredMemory = (long)(((
  					((double) reconDimensionX) * reconDimensionY * ((double) reconDimensionZ) * 4) 
  					+ (((double)Configuration.getGlobalConfiguration().getGeometry().getDetectorHeight()) * Configuration.getGlobalConfiguration().getGeometry().getDetectorWidth() * 4)));
  			if (debug) {
  				CONRAD.log("Total available Memory on OpenCL card:" + availableMemory);
  				CONRAD.log("Required Memory on OpenCL card:" + requiredMemory);
  			}
  			if (!forceSmallVolume) {
  				if (requiredMemory > availableMemory){
  					nSteps = (int)OpenCLUtil.iDivUp (requiredMemory, availableMemory);
  					if (debug) CONRAD.log("Switching to large volume mode with nSteps = " + nSteps);
  					largeVolumeMode = true;
  				}
  			}
  			if (debug) {
  				//TODO replace
  				/*
  				CUdevprop prop = new CUdevprop();
  				JCudaDriver.cuDeviceGetProperties(prop, dev);
  				System.out.println(prop.toFormattedString());
  				 */
  			}
  
  			// create the computing kernel
  			kernelFunction = program.createCLKernel("backprojectKernel");
  
  			// create the reconstruction volume;
  			int memorysize = reconDimensionX * reconDimensionY * reconDimensionZ * 4;
  			
  			//h_volume = new float[reconDimensionX * reconDimensionY * reconDimensionZ];	
  	
  			//int gat_ign = 4;
  			h_volume = new float[(2*gat_ign +1) *reconDimensionX*reconDimensionY*reconDimensionZ];
  			for(int i = 0; i < h_volume.length; i = i + 2*gat_ign+1){
  				h_volume[i] = 0.0f;
  				for(int j = 0; j < gat_ign; j++){
  					h_volume[i + j] = -100001.0f;
  				}
  				for(int j = gat_ign; j < 2*gat_ign; j++){
  					h_volume[i + j] = 100001.0f;
  				}
  			}
  			
  			destBuffer = context.createFloatBuffer(reconDimensionX*reconDimensionY*reconDimensionZ, Mem.WRITE_ONLY);
  
  			// compute adapted volume size 
  			//    volume size in x = multiple of bpBlockSize[0]
  			//    volume size in y = multiple of bpBlockSize[1]
  
  			System.out.println("RecondimX: " + reconDimensionX);
  			int adaptedVolSize[] = new int[3];
  			if ((reconDimensionX % bpBlockSize[0] ) == 0){
  				adaptedVolSize[0] = reconDimensionX;
  			} else {
  				adaptedVolSize[0] = ((reconDimensionX / bpBlockSize[0]) + 1) * bpBlockSize[0];
  			}
  			System.out.println("AdaptedVolSize0: " + adaptedVolSize[0]);
  			
  			System.out.println("RecondimX: " + reconDimensionX);
  			if ((reconDimensionY % bpBlockSize[1] ) == 0){
  				adaptedVolSize[1] = reconDimensionY;
  			} else {
  				adaptedVolSize[1] = ((reconDimensionY / bpBlockSize[1]) + 1) * bpBlockSize[1];
  			}
  			System.out.println("AdaptedVolSize1: " + adaptedVolSize[0]);
  			adaptedVolSize[2] = reconDimensionZ;
  			int volStrideHost [] = new int[2];
  			// compute volstride and copy it to constant memory
  			volStrideHost[0] = adaptedVolSize[0];
  			volStrideHost[1] = adaptedVolSize[0] * adaptedVolSize[1];
  
  			// copy volume to device
  			volumePointer = context.createFloatBuffer(h_volume.length, Mem.WRITE_ONLY);
  			volumePointer.getBuffer().put(h_volume);
  			volumePointer.getBuffer().rewind();
  
  			// copy volume stride to device
  			volStride = context.createIntBuffer(volStrideHost.length, Mem.READ_ONLY);
  			volStride.getBuffer().put(volStrideHost);
  			volStride.getBuffer().rewind();
  
  			commandQueue.
  			putWriteBuffer(volumePointer, true).
  			putWriteBuffer(volStride, true).
  			finish();
  
  			initialized = true;
  		}
  
  	}
  
  	private synchronized void unload(){
  		if (initialized) {
  
  			if ((projectionVolume != null) && (!largeVolumeMode)) {
  				
  				int reconDimensionX = getGeometry().getReconDimensionX();
  				int reconDimensionY = getGeometry().getReconDimensionY();
  				int reconDimensionZ = getGeometry().getReconDimensionZ();
  
  				
  				commandQueue.putReadBuffer(volumePointer, true).finish();
  				volumePointer.getBuffer().rewind();
  				volumePointer.getBuffer().get(h_volume);
  				volumePointer.getBuffer().rewind();
  				destBuffer.getBuffer().rewind();
  				
  				int length = h_volume.length;
  				//int gat_ign = 4;
  				float[] values = new float[reconDimensionX*reconDimensionY*reconDimensionZ];
  				//h_volume = new float[length/7];
  				h_volume = new float[reconDimensionX*reconDimensionY*reconDimensionZ];
  				
  				System.out.println("Length/(2*gat_ign+1): " + length/(2*gat_ign+1));
  				System.out.println("recDimensions: " + reconDimensionX*reconDimensionY*reconDimensionZ);
  				
  				
  				//subtract the volumina of the 6 ignore volumina
  				//create the correct grid afterwards
  				/*int v = 0;
  				for(int i = 0; i < length; i = i + 2*gat_ign+1){
  					float reconValue = volumePointer.getBuffer().get();
  					for(int ign = 0; ign < 2*gat_ign; ign++){
  						float val = volumePointer.getBuffer().get();
  						if(val < 100000.0f && val > -100000.0f){
  							//subtract the values that shall be ignored
  							reconValue = reconValue - val;
  						}
  					}
  					if(v < values.length){
  						h_volume[v] = reconValue;
  						v++;
  					}
  				}*/
  				
  				/*v = 0;
  				for (int x=0;x < projectionVolume.getSize()[0];++x) {	
  					for (int y=0;y < projectionVolume.getSize()[1];++y) {			//TODO MOEGLICHE FEHLERQUELLE
  						for(int z = 0; z< projectionVolume.getSize()[2]; z++){
  							projectionVolume.setAtIndex(x,y,z,values[v]);
  							v ++;
  						}
  					}
  				}*/
  
  
  
  				/*int width = projectionVolume.getSize()[0];
  				int height = projectionVolume.getSize()[1];
  				if (this.useVOImap) {
  					for (int k = 0; k < projectionVolume.getSize()[2]; k++){
  						for (int j = 0; j < height; j++){
  							for (int i = 0; i < width; i++){			
  								float value = h_volume[(((height * k) + j) * width) + i];
  								if (voiMap[i][j][k]) {
  									projectionVolume.setAtIndex(i, j, k, value);
  								} else {
  									projectionVolume.setAtIndex(i, j, k, 0);
  								}
  							}
  						}
  					}
  				} else {
  					for (int k = 0; k < projectionVolume.getSize()[2]; k++){
  						for (int j = 0; j < height; j++){
  							for (int i = 0; i < width; i++){			
  								float value = h_volume[(((height * k) + j) * width) + i];
  								projectionVolume.setAtIndex(i, j, k, value);
  							}
  						}
  					}
  				}*/
  				
  
  				destBuffer.getBuffer().rewind();
  				for (int x=0; x < reconDimensionX ;++x) {	
  					for (int y=0; y < reconDimensionY;++y) {
  						for(int z = 0; z< reconDimensionZ; z++){
  							//grid.setAtIndex(x, y, z, imgBuffer.getBuffer().get());
  							projectionVolume.setAtIndex(x, y, z, destBuffer.getBuffer().get());
  						}
  					}
  				}
  				
  				
  				
  				
  				
  			} else {
  				CONRAD.log("Check ProjectionVolume. It seems null.");
  			}
  
  
  			h_volume = null;
  
  
  			// free memory on device
  			commandQueue.release();
  
  			if (projectionTex != null)
  				projectionTex.release();
  			if (projectionMatrix != null)
  				projectionMatrix.release();
  			if (volStride != null)
  				volStride.release();
  			if (projectionArray != null)
  				projectionArray.release();
  			if (volumePointer != null)
  				volumePointer.release();
  
  			kernelFunction.release();
  			program.release();
  			// destory context
  			context.release();
  
  
  			commandQueue = null;
  			projectionArray = null;
  			projectionMatrix = null;
  			projectionTex = null;
  			volStride = null;
  			volumePointer = null;
  			kernelFunction = null;
  			program = null;
  			context = null;
  
  
  			initialized = false;
  		}
  	}
  
  	protected synchronized void initProjectionMatrix(int projectionNumber){
  		// load projection Matrix for current Projection.
  		if (getGeometry().getProjectionMatrix(projectionNumber)== null) {
  			CONRAD.log("No geometry found for projection " +projectionNumber + ". Skipping.");
  			return;
  		}
  		SimpleMatrix pMat = getGeometry().getProjectionMatrix(projectionNumber).computeP();
  		float [] pMatFloat = new float[pMat.getCols() * pMat.getRows()];
  		for (int j = 0; j< pMat.getRows(); j++) {
  			for (int i = 0; i< pMat.getCols(); i++) {
  
  				pMatFloat[(j * pMat.getCols()) + i] = (float) pMat.getElement(j, i);
  			}
  		}
  
  		// Obtain the global pointer to the view matrix from
  		// the module
  		if (projectionMatrix == null)
  			projectionMatrix = context.createFloatBuffer(pMatFloat.length, Mem.READ_ONLY);
  
  		projectionMatrix.getBuffer().put(pMatFloat);
  		projectionMatrix.getBuffer().rewind();
  		commandQueue.putWriteBuffer(projectionMatrix, true).finish();
  		//System.out.println("Uploading matrix " + projectionNumber);
  	}
  
  	private synchronized void initProjectionData(Grid2D projection){
  		initialize(projection);
  		if (projection != null){ 
  			float [] proj= new float[projection.getWidth() * projection.getHeight()];
  
  			for(int i = 0; i< projection.getWidth(); i++){
  				for (int j =0; j < projection.getHeight(); j++){
  					proj[(j*projection.getWidth()) + i] = projection.getPixelValue(i, j);
  				}
  			}
  
  			if (projectionArray == null) {
  				// Create the array that will contain the
  				// projection data. 
  				projectionArray = context.createFloatBuffer(projection.getWidth()*projection.getHeight(), Mem.READ_ONLY);
  			}
  
  			// Copy the projection data to the array 
  			projectionArray.getBuffer().put(proj);
  			projectionArray.getBuffer().rewind();
  
  			// set the texture
  			CLImageFormat format = new CLImageFormat(ChannelOrder.INTENSITY, ChannelType.FLOAT);
  			projectionTex = context.createImage2d(projectionArray.getBuffer(), projection.getWidth(), projection.getHeight(), format, Mem.READ_ONLY);
  			//projectionArray.release();
  
  		} else {
  			CONRAD.log("Projection was null!!");
  		}
  	}
  
  	@Override
  	public String getName() {
  		return "OpenCL Backprojector";
  	}
  
  
  	@Override
  	public String getBibtexCitation() {
  		String bibtex = "@inproceedings{Rohkohl08-CCR,\n" +
  		"  author = {{Scherl}, H. and {Keck}, B. and {Kowarschik}, M. and {Hornegger}, J.},\n" +
  		"  title = {{Fast GPU-Based CT Reconstruction using the Common Unified Device Architecture (CUDA)}},\n" +
  		"  booktitle = {{Nuclear Science Symposium, Medical Imaging Conference 2007}},\n" +
  		"  publisher = {IEEE},\n" +
  		"  volume={6},\n" +
  		"  address = {Honolulu, HI, United States},\n" +
  		"  year = {2007}\n" +
  		"  pages= {4464--4466},\n" +
  		"}";
  		return bibtex;
  	}
  
  	@Override
  	public String getMedlineCitation() {
  		return "Scherl H, Keck B, Kowarschik M, Hornegger J. Fast GPU-Based CT Reconstruction using the Common Unified Device Architecture (CUDA). In Nuclear Science Symposium, Medical Imaging Conference Record, IEEE, Honolulu, HI, United States, 2008 6:4464-6.";
  	}
  
  	public void waitForResult() {
  		OpenCLRun();
  	}
  
  	@Override
  	public void backproject(Grid2D projection, int projectionNumber)
  	{
  		appendProjection(projection, projectionNumber);
  	}
  
  	private void appendProjection(Grid2D projection, int projectionNumber){	
  		projections.add(projection, projectionNumber);
  		projectionsAvailable.add(new Integer(projectionNumber));
  	}
  
  	private synchronized void projectSingleProjection(int projectionNumber, int dimz){
  		// load projection matrix
  		initProjectionMatrix(projectionNumber);
  		// load projection
  		Grid2D projection = (Grid2D)projections.get(projectionNumber).clone(); 
  
  		// Correct for constant part of distance weighting + For angular sampling
  		double D =  getGeometry().getSourceToDetectorDistance();
  		NumericPointwiseOperators.multiplyBy(projection, (float)(10 * D*D * 2* Math.PI * getGeometry().getPixelDimensionX() / getGeometry().getNumProjectionMatrices()));
  
  		initProjectionData(projection);
  		//System.out.println("Uploading projection " + projectionNumber);
  		if (!largeVolumeMode) {
  			projections.remove(projectionNumber);
  		}
  		// backproject for each slice
  		// OpenCL Grids are only two dimensional!
  		int reconDimensionZ = dimz;
  		double voxelSpacingX = getGeometry().getVoxelSpacingX();
  		double voxelSpacingY = getGeometry().getVoxelSpacingY();
  		double voxelSpacingZ = getGeometry().getVoxelSpacingZ();
  
  		// write kernel parameters
  		kernelFunction.rewind();
  		kernelFunction
  		.putArg(volumePointer)
  		.putArg(destBuffer)
  		.putArg((int) lineOffset)
  		.putArg(reconDimensionZ)
  		.putArg((float) voxelSpacingX)
  		.putArg((float) voxelSpacingY)
  		.putArg((float) voxelSpacingZ)
  		.putArg((float) offsetX)
  		.putArg((float) offsetY)
  		.putArg((float) offsetZ)
  		.putArg(projectionTex)
  		.putArg(volStride)
  		.putArg(projectionMatrix);
  
  		int[] realLocalSize = {Math.min(device.getMaxWorkGroupSize(),bpBlockSize[0]), Math.min(device.getMaxWorkGroupSize(),bpBlockSize[1])};
  		// rounded up to the nearest multiple of localWorkSize
  		int[] globalWorkSize = {getGeometry().getReconDimensionX(), getGeometry().getReconDimensionY()}; 
  		if ((globalWorkSize[0] % bpBlockSize[0] ) != 0){
  			globalWorkSize[0] = ((globalWorkSize[0] / bpBlockSize[0]) + 1) * bpBlockSize[0];
  		}
  		if ((globalWorkSize[1] % bpBlockSize[1] ) != 0){
  			globalWorkSize[1] = ((globalWorkSize[1] / bpBlockSize[1]) + 1) * bpBlockSize[1];
  		}
  
  		// Call the OpenCL kernel, writing the results into the volume which is pointed at
  		commandQueue
  		.putWriteImage(projectionTex, true)
  		.finish()
  		.put2DRangeKernel(kernelFunction, 0, 0, globalWorkSize[0], globalWorkSize[1], realLocalSize[0], realLocalSize[1])
  		//.finish()
  		//.putReadBuffer(dOut, true)
  		.finish();
  		projectionTex.release();
  		projectionTex = null;
  	}
  
  	public void OpenCLRun() {
  		try {
  			while (projectionsAvailable.size() > 0) {
  				Thread.sleep(CONRAD.INVERSE_SPEEDUP);
  				if (showStatus) {
  					float status = (float) (1.0 / projections.size());
  					if (largeVolumeMode) {
  						IJ.showStatus("Streaming Projections to OpenCL Buffer");
  					} else {
  						IJ.showStatus("Backprojecting with OpenCL");
  					}
  					IJ.showProgress(status);
  				}
  				if (!largeVolumeMode) {	
  					//process all projections
  					workOnProjectionData();
  				} else {
  					checkProjectionData();
  				}
  			}
  			CONRAD.log("large Volume " + largeVolumeMode);
  			//if (largeVolumeMode){
  
  		} catch (InterruptedException e) {
  
  			e.printStackTrace();
  		}
  		if (showStatus) IJ.showProgress(1.0);
  		unload();
  		if (debug) CONRAD.log("Unloaded");
  	}
  
  	private synchronized void workOnProjectionData(){
  		if (projectionsAvailable.size() > 0){
  			Integer current = projectionsAvailable.get(0);
  			projectionsAvailable.remove(0);
  			projectSingleProjection(current.intValue(),  
  					getGeometry().getReconDimensionZ());
  			projectionsDone.add(current);
  		}	
  	}
  
  	private synchronized void checkProjectionData(){
  		if (projectionsAvailable.size() > 0){
  			Integer current = projectionsAvailable.get(0);
  			projectionsAvailable.remove(current);
  			projectionsDone.add(current);
  		}	
  	}
  
  	public void reconstructOffline(ImagePlus imagePlus) throws Exception {
  		ImagePlusDataSink sink = new ImagePlusDataSink();
  		configure();
  		init();
  		for (int i = 0; i < imagePlus.getStackSize(); i++){
  			backproject(ImageUtil.wrapImageProcessor(imagePlus.getStack().getProcessor(i+1)), i);
  		}
  		waitForResult();
  		if (Configuration.getGlobalConfiguration().getUseHounsfieldScaling()) applyHounsfieldScaling();
  		int [] size = projectionVolume.getSize();
  		//System.out.println(size [0] + " " + size [1] + " " + size[2]);
  		for (int k = 0; k < projectionVolume.getSize()[2]; k++){
  			FloatProcessor fl = new FloatProcessor(projectionVolume.getSize()[0], projectionVolume.getSize()[1]);
  			for (int j = 0; j< projectionVolume.getSize()[1]; j++){
  				for (int i = 0; i< projectionVolume.getSize()[0]; i++){
  					fl.putPixelValue(i, j, projectionVolume.getAtIndex(i, j, k));
  				}
  			}
  			sink.process(projectionVolume.getSubGrid(k), k);
  		}
  		sink.close();
  		ImagePlus revan = ImageUtil.wrapGrid3D(sink.getResult(), "Reconstruction of " + imagePlus.getTitle());
  		revan.setTitle(imagePlus.getTitle() + " reconstructed");
  		revan.show();
  	}
  
  	@Override
  	protected void reconstruct() throws Exception {
  		init();
  		for (int i = 0; i < nImages; i++){
  			backproject(inputQueue.get(i), i);
  		}
  		waitForResult();
  		if (Configuration.getGlobalConfiguration().getUseHounsfieldScaling()) applyHounsfieldScaling();
  		int [] size = projectionVolume.getSize();
  
  		for (int k = 0; k < size[2]; k++){
  			sink.process(projectionVolume.getSubGrid(k), k);
  		}
  		sink.close();
  	}
  
  	@Override
  	public String getToolName(){
  		return "OpenCL Backprojector with Streak Reduction";
  	}
  
  	/**
  	 * @return the forceSmallVolume
  	 */
  	public boolean isForceSmallVolume() {
  		return forceSmallVolume;
  	}
  
  	/**
  	 * @param forceSmallVolume the forceSmallVolume to set
  	 */
  	public void setForceSmallVolume(boolean forceSmallVolume) {
  		this.forceSmallVolume = forceSmallVolume;
  	}
  
  }
  /*
   * Copyright (C) 2010-2014 Martin Berger
   * CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
  */
  