private synchronized void unload() {
    if (initialized) {

      if ((projectionVolume != null) && (!largeVolumeMode)) {

        commandQueue.putReadBuffer(volumePointer, true).finish();
        volumePointer.getBuffer().rewind();
        volumePointer.getBuffer().get(h_volume);
        volumePointer.getBuffer().rewind();

        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);
              }
            }
          }
        }
      } else {
        System.out.println("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;
    }
  }
  public void OpenCLRun(double[] motionfield) {
    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) {
          workOnProjectionData(motionfield);
        } else {
          checkProjectionData();
        }
      }
      //			System.out.println("large Volume " + largeVolumeMode);
      if (largeVolumeMode) {
        // we have collected all projections.
        // now we can reconstruct subvolumes and stich them together.
        int reconDimensionZ = getGeometry().getReconDimensionZ();
        double voxelSpacingX = getGeometry().getVoxelSpacingX();
        double voxelSpacingY = getGeometry().getVoxelSpacingY();
        double voxelSpacingZ = getGeometry().getVoxelSpacingZ();
        useVOImap = false;
        initialize(projections.get(0));
        double originalOffsetZ = offsetZ;
        double originalReconDimZ = reconDimensionZ;
        reconDimensionZ = subVolumeZ;
        int maxProjectionNumber = projections.size();
        float all = nSteps * maxProjectionNumber * 2;
        for (int n = 0; n < nSteps; n++) { // For each subvolume
          // set all to 0;
          Arrays.fill(h_volume, 0);

          volumePointer.getBuffer().rewind();
          volumePointer.getBuffer().put(h_volume);
          volumePointer.getBuffer().rewind();
          commandQueue.putWriteBuffer(volumePointer, true).finish();

          offsetZ = originalOffsetZ - (reconDimensionZ * voxelSpacingZ * n);
          for (int p = 0; p < maxProjectionNumber; p++) { // For all projections
            float currentStep = (n * maxProjectionNumber * 2) + p;
            if (showStatus) {
              IJ.showStatus("Backprojecting with OpenCL");
              IJ.showProgress(currentStep / all);
            }
            // System.out.println("Current: " + p);
            float respoffset = (float) Math.round(motionfield[p] / voxelSpacingZ);
            try {
              projectSingleProjection(p, reconDimensionZ, respoffset);
            } catch (Exception e) {
              System.out.println("Backprojection of projection " + p + " was not successful.");
              e.printStackTrace();
            }
          }
          // Gather volume
          commandQueue.putReadBuffer(volumePointer, true).finish();
          volumePointer.getBuffer().rewind();
          volumePointer.getBuffer().get(h_volume);
          volumePointer.getBuffer().rewind();

          // move data to ImagePlus;
          if (projectionVolume != null) {
            for (int k = 0; k < reconDimensionZ; k++) {
              int index = (n * subVolumeZ) + k;
              if (showStatus) {
                float currentStep = (n * maxProjectionNumber * 2) + maxProjectionNumber + k;
                IJ.showStatus("Fetching Volume from OpenCL");
                IJ.showProgress(currentStep / all);
              }
              if (index < originalReconDimZ) {
                for (int j = 0; j < projectionVolume.getSize()[1]; j++) {
                  for (int i = 0; i < projectionVolume.getSize()[0]; i++) {
                    float value =
                        h_volume[
                            (((projectionVolume.getSize()[1] * k) + j)
                                    * projectionVolume.getSize()[0])
                                + i];
                    double[][] voxel = new double[4][1];
                    voxel[0][0] = (voxelSpacingX * i) - offsetX;
                    voxel[1][0] = (voxelSpacingY * j) - offsetY;
                    voxel[2][0] = (voxelSpacingZ * index) - originalOffsetZ;

                    // exception for the case "interestedInVolume == null" and largeVolume is
                    // enabled
                    if (interestedInVolume == null) {
                      projectionVolume.setAtIndex(i, j, index, value);
                    } else {
                      if (interestedInVolume.contains(voxel[0][0], voxel[1][0], voxel[2][0])) {
                        projectionVolume.setAtIndex(i, j, index, value);
                      } else {
                        projectionVolume.setAtIndex(i, j, index, 0);
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }

    } catch (InterruptedException e) {

      e.printStackTrace();
    }
    if (showStatus) IJ.showProgress(1.0);
    unload();
    if (debug) System.out.println("Unloaded");
  }
  public Grid2D openCLBackprojection(
      OpenCLGrid2D filteredSinogramm,
      int widthPhantom,
      int heightPhantom,
      int worksize,
      float detectorSpacing,
      int numberOfPixel,
      int numberProjections,
      float scanAngle,
      double[] spacing,
      double[] origin) {
    // create context
    CLContext context = OpenCLUtil.getStaticContext();

    // select device
    CLDevice device = context.getMaxFlopsDevice();

    // define local and global sizes

    double spacingAngle = (double) (scanAngle / numberProjections);
    double originDetector = -(detectorSpacing * numberOfPixel) / 2.0;

    int imageSize = widthPhantom * heightPhantom;
    int localWorkSize = Math.min(device.getMaxWorkGroupSize(), worksize);
    int globalWorkSizeW =
        OpenCLUtil.roundUp(
            localWorkSize, widthPhantom); // rounded up to the nearest multiple of localWorkSize
    int globalWorkSizeH = OpenCLUtil.roundUp(localWorkSize, heightPhantom);

    // load sources, create and build programm

    try {
      this.program =
          context.createProgram(this.getClass().getResourceAsStream("exercise4.cl")).build();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
      System.exit(-1);
    }

    // create image from input grid
    // CLImageFormat format = new CLImageFormat(ChannelOrder.INTENSITY, ChannelType.FLOAT);

    // create output image
    CLBuffer<FloatBuffer> output = context.createFloatBuffer(imageSize, Mem.WRITE_ONLY);
    if (kernel == null) {
      kernel = program.createCLKernel("parallelBackProjection");
    }

    // createCommandQueue
    CLCommandQueue queue = device.createCommandQueue();
    filteredSinogramm.getDelegate().prepareForDeviceOperation();
    // put memory on the graphics card

    kernel
        .putArg(filteredSinogramm.getDelegate().getCLBuffer())
        .putArg(output)
        .putArg(numberProjections)
        .putArg(numberOfPixel)
        .putArg(scanAngle)
        .putArg(widthPhantom)
        .putArg(heightPhantom)
        .putArg(spacing[0])
        .putArg(spacing[1])
        .putArg(origin[0])
        .putArg(origin[1])
        .putArg(detectorSpacing)
        .putArg(spacingAngle)
        .putArg(originDetector)
        .putArg(0.d);

    kernel.rewind();

    queue
        .put2DRangeKernel(
            kernel, 0, 0, globalWorkSizeW, globalWorkSizeH, localWorkSize, localWorkSize)
        .putBarrier()
        .finish();
    // put memory from graphic card to host
    queue.putReadBuffer(output, true).finish();

    output.getBuffer().rewind();

    for (int i = 0; i < image.getSize()[1]; ++i) {
      for (int j = 0; j < image.getSize()[0]; j++) {
        image.setAtIndex(j, i, output.getBuffer().get());
      }
    }

    output.release();
    queue.release();

    return image;
  }