/*
* Copyright (C) 2014 Andreas Maier
* CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
*/
package edu.stanford.rsl.tutorial.motion.compensation;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.FloatBuffer;
import com.jogamp.opencl.CLBuffer;
import edu.stanford.rsl.conrad.data.numeric.Grid2D;
import edu.stanford.rsl.conrad.data.numeric.NumericPointwiseOperators;
import edu.stanford.rsl.conrad.geometry.motion.MotionField;
import edu.stanford.rsl.conrad.geometry.motion.OpenCLParzenWindowMotionField;
import edu.stanford.rsl.conrad.geometry.motion.ParzenWindowMotionField;
import edu.stanford.rsl.conrad.opencl.OpenCLBackProjector;
import edu.stanford.rsl.conrad.opencl.OpenCLUtil;
import edu.stanford.rsl.conrad.utils.Configuration;
public class OpenCLMotionCompensatedBackProjector extends OpenCLBackProjector {
/**
*
*/
private static final long serialVersionUID = 3263500016065695870L;
protected MotionField motion = null;
protected double referenceTime = 0;
protected CLBuffer<FloatBuffer> motionParameters = null;
/**
* @return the motion
*/
public MotionField getMotion() {
return motion;
}
/**
* @param motion the motion to set
*/
public void setMotion(MotionField motion) {
this.motion = motion;
}
protected String readCompleteRessourceAsString(String resource) throws IOException{
InputStream inStream = OpenCLMotionCompensatedBackProjector.class.getResourceAsStream(resource);
BufferedReader br = new BufferedReader(new InputStreamReader(inStream));
String content = "";
String line = br.readLine();
while (line != null){
content += line + "\n";
line = br.readLine();
};
return content;
}
@Override
protected void createProgram() throws IOException{
// initialize the program
if (program==null || !program.getContext().equals(this.context)){
String motionProgram = "";
if (motion instanceof ParzenWindowMotionField){
motionProgram = readCompleteRessourceAsString("applyMotionDenseMotionField.cl");
}
String backprojectProgram = readCompleteRessourceAsString("generalMotionCompensatedBackprojectCL.cl");
program = context.createProgram(motionProgram + backprojectProgram).build();
}
}
protected void initMotionParameters(double referenceTime, double currentTime){
Configuration config = Configuration.getGlobalConfiguration();
if (motion instanceof ParzenWindowMotionField){
if (motionParameters != null) {
motionParameters.release();
motionParameters = null;
}
ParzenWindowMotionField motion = (ParzenWindowMotionField) this.motion;
OpenCLParzenWindowMotionField cl;
try {
cl = new OpenCLParzenWindowMotionField(motion, context, device);
int factor = 16;
motionParameters = cl.getMotionFieldAsArrayReduceZGridXY(referenceTime, currentTime, OpenCLUtil.iDivUp(config.getGeometry().getReconDimensionX(),factor), OpenCLUtil.iDivUp(config.getGeometry().getReconDimensionY(),factor), commandQueue, true);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@Override
protected synchronized void projectSingleProjection(int projectionNumber, int dimz){
// load projection matrix
initProjectionMatrix(projectionNumber);
// Correct for constant part of distance weighting + For angular sampling
double D = getGeometry().getSourceToDetectorDistance();
float projectionMultiplier = (float)(10 * D*D * 2* Math.PI * getGeometry().getPixelDimensionX() / getGeometry().getNumProjectionMatrices());
initProjectionData(projections.get(projectionNumber));
//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();
initMotionParameters(referenceTime, ((float)projectionNumber)/(projections.size()-1));
// write kernel parameters
kernelFunction.rewind();
kernelFunction
.putArg(volumePointer)
.putArg(getGeometry().getReconDimensionX())
.putArg(getGeometry().getReconDimensionY())
.putArg(reconDimensionZ)
.putArg((int) lineOffset)
.putArg((float) voxelSpacingX)
.putArg((float) voxelSpacingY)
.putArg((float) voxelSpacingZ)
.putArg((float) offsetX)
.putArg((float) offsetY)
.putArg((float) offsetZ)
.putArg(projectionTex)
.putArg(projectionMatrix)
.putArg(projectionMultiplier)
.putArg(motionParameters);
int[] realLocalSize = new int[2];
realLocalSize[0] = Math.min(device.getMaxWorkGroupSize(),bpBlockSize[0]);
realLocalSize[1] = Math.max(1, Math.min(device.getMaxWorkGroupSize()/realLocalSize[0], bpBlockSize[1]));
// rounded up to the nearest multiple of localWorkSize
int[] globalWorkSize = {getGeometry().getReconDimensionX(), getGeometry().getReconDimensionY()};
if ((globalWorkSize[0] % realLocalSize[0] ) != 0){
globalWorkSize[0] = ((globalWorkSize[0] / realLocalSize[0]) + 1) * realLocalSize[0];
}
if ((globalWorkSize[1] % realLocalSize[1] ) != 0){
globalWorkSize[1] = ((globalWorkSize[1] / realLocalSize[1]) + 1) * realLocalSize[1];
}
// Call the OpenCL kernel, writing the results into the volume which is pointed at
commandQueue
.putWriteImage(projectionTex, true)
.put2DRangeKernel(kernelFunction, 0, 0, globalWorkSize[0], globalWorkSize[1], realLocalSize[0], realLocalSize[1])
//.finish()
//.putReadBuffer(dOut, true)
.finish();
}
/**
* @return the referenceTime
*/
public double getReferenceTime() {
return referenceTime;
}
@Override
public String getToolName(){
return "Motion Compensated OpenCL Backprojector";
}
/**
* @param referenceTime the referenceTime to set
*/
public void setReferenceTime(double referenceTime) {
this.referenceTime = referenceTime;
}
@Override
protected synchronized void unload() {
if(motionParameters!=null && !motionParameters.isReleased())
motionParameters.release();
super.unload();
}
}