package edu.stanford.rsl.conrad.reconstruction;
import java.text.NumberFormat;
import java.util.Locale;
import edu.stanford.rsl.conrad.data.numeric.Grid2D;
import edu.stanford.rsl.conrad.data.numeric.Grid3D;
import edu.stanford.rsl.conrad.parallel.ParallelThreadExecutor;
import edu.stanford.rsl.conrad.parallel.ParallelizableRunnable;
import edu.stanford.rsl.conrad.parallel.SimpleParallelThread;
import edu.stanford.rsl.conrad.reconstruction.voi.VolumeOfInterest;
import edu.stanford.rsl.conrad.utils.CONRAD;
import edu.stanford.rsl.conrad.utils.Configuration;
import edu.stanford.rsl.conrad.utils.RegKeys;
/**
* This FBP-based mathod splits the reconstruction volume into sub volumes which can be processed in parallel to speed up the reconstruction further.
* @author akmaier
*
*/
public class SubVolumeBackprojector extends VOIBasedReconstructionFilter {
/**
*
*/
private static final long serialVersionUID = -3452972435131680120L;
protected int numThreads = - 1;
protected VOIBasedReconstructionFilter [] projectors;
protected Grid3D [] subVolumes;
protected boolean debug = false;
public SubVolumeBackprojector() {
numThreads = CONRAD.getNumberOfThreads();
projectors = new VOIBasedReconstructionFilter[numThreads];
subVolumes = new Grid3D[numThreads];
for (int i = 0; i < numThreads; i++){
projectors[i] = new VOIBasedReconstructionFilter();
}
}
@Override
public String getName(){
return "Parallel CPU-based Sub-volume Backprojector";
}
public void configure() throws Exception{
Configuration config = Configuration.getGlobalConfiguration();
boolean success = true;
NumberFormat nf = NumberFormat.getInstance(Locale.US);
nf.setMaximumFractionDigits(1);
nf.setMinimumFractionDigits(1);
nf.setMaximumIntegerDigits(1);
nf.setMinimumIntegerDigits(1);
if (debug) System.out.println("loading");
// volume of interest is optional
if (config.getVolumeOfInterestFileName() != null) {
this.setMaximumVolumeOfInterest(config.getVolumeOfInterestFileName());
} else {
String filename = config.getRegistry().get(RegKeys.PATH_TO_CALIBRATION) + "/" + config.getDeviceSerialNumber() + "/active/-903/" + "/d/" + config.getIntensifierSize() + "/" + nf.format(config.getGeometry().getAverageAngularIncrement()) + "/maxVOI.txt";
this.setMaximumVolumeOfInterest(filename);
}
for (int i = 0; i < numThreads; i++){
projectors[i].setMaximumVolumeOfInterest(interestedInVolume);
System.out.println("new Geometry");
}
initializeProjectionVolume();
configured = success;
}
public void initializeProjectionVolume(){
super.initializeProjectionVolume();
int reconDimensionX = getGeometry().getReconDimensionX();
int reconDimensionY = getGeometry().getReconDimensionY();
int reconDimensionZ = getGeometry().getReconDimensionZ();
double voxelSpacingX = getGeometry().getVoxelSpacingX();
double voxelSpacingY = getGeometry().getVoxelSpacingY();
double voxelSpacingZ = getGeometry().getVoxelSpacingZ();
int subVolumeReconDimensionX = (int) Math.ceil((reconDimensionX + 0.0) / numThreads);
int lastSubVolumeReconDimensionX = reconDimensionX - ((numThreads - 1) * subVolumeReconDimensionX);
for (int i = 0; i < numThreads; i++){
if (debug) {
projectors[i].setMaxI(subVolumeReconDimensionX);
projectors[i].initializeProjectionVolume();
} else {
if (i < numThreads - 1)
subVolumes[i] = new Grid3D(subVolumeReconDimensionX,reconDimensionY,reconDimensionZ);
else
subVolumes[i] = new Grid3D(lastSubVolumeReconDimensionX,reconDimensionY,reconDimensionZ);
projectors[i].setProjectionVolume(subVolumes[i]);
projectors[i].offsetX = (-getGeometry().getOriginX()) - (i * subVolumeReconDimensionX * voxelSpacingX) ;
projectors[i].offsetY = -getGeometry().getOriginY();
projectors[i].offsetZ = -getGeometry().getOriginZ();
projectors[i].setMaxI(subVolumeReconDimensionX);
projectors[i].maxK = this.maxK;
projectors[i].maxJ = this.maxJ;
projectors[i].lineOffset = lineOffset;
projectors[i].init = true;
if (i < numThreads - 1) {
projectors[i].setMaxI(subVolumeReconDimensionX);
} else {
projectors[i].setMaxI(lastSubVolumeReconDimensionX);
}
}
}
volumeRewritten = false;
}
@Override
public void prepareForSerialization(){
super.prepareForSerialization();
for (int i = 0; i < numThreads; i++){
subVolumes[i] = null;
if (projectors[i] != null) projectors[i].prepareForSerialization();
}
volumeRewritten = false;
}
protected boolean volumeRewritten = false;
@Override
protected void reconstruct() throws Exception {
if (!init){
initialize(inputQueue.get(0));
}
initializeProjectionVolume();
ParallelizableRunnable [] runnables = new ParallelizableRunnable[numThreads];
for (int j= 0; j<numThreads; j++) {
runnables[j]= new SimpleParallelThread(j) {
@Override
public void execute() {
for (int i = 0; i < nImages; i++){
projectors[threadNum].backproject(inputQueue.get(i), i);
}
}
};
}
ParallelThreadExecutor executor = new ParallelThreadExecutor(runnables);
executor.execute();
if (!volumeRewritten) {
//System.out.println("Rewriting Volume");
int subVolumeReconDimensionX = (int) Math.ceil((getGeometry().getReconDimensionX() + 0.0) / numThreads);
int lastSubVolumeReconDimensionX = getGeometry().getReconDimensionX() - ((numThreads - 1) * subVolumeReconDimensionX);
for (int i = 0; i < numThreads; i++){
int offsetx = subVolumeReconDimensionX * i;
int currentlimit = subVolumeReconDimensionX;
if (i == numThreads - 1) currentlimit = lastSubVolumeReconDimensionX;
for (int z = 0; z < getGeometry().getReconDimensionZ(); z ++){
for (int y = 0; y < getGeometry().getReconDimensionY(); y++){
for (int x = 0; x < currentlimit; x ++){
float value = subVolumes[i].getAtIndex(x, y, z);
projectionVolume.setAtIndex(x + offsetx, y, z, value);
}
}
}
}
volumeRewritten = true;
}
if (Configuration.getGlobalConfiguration().getUseHounsfieldScaling()) applyHounsfieldScaling();
for (int k = 0; k < projectionVolume.getSize()[2]; k++){
sink.process(projectionVolume.getSubGrid(k), k);
}
init = false;
}
@Override
public String getBibtexCitation() {
String bibtex = "@inproceedings{Scherl07-FGB,\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.";
}
@Override
public String getToolName(){
return "Subvolume CPU-based Backprojector";
}
@Override
public void setMaximumVolumeOfInterest(VolumeOfInterest maxVOI) {
for (int i = 0; i < projectors.length; i++) {
projectors[i].setMaximumVolumeOfInterest(maxVOI);
projectors[i].useVOImap = true;
}
}
@Override
public void setFastVOIMode(boolean fastVOIMode) {
for (int i = 0; i < projectors.length; i++) {
projectors[i].setFastVOIMode(fastVOIMode);
}
}
}
/*
* Copyright (C) 2010-2014 Andreas Maier
* CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
*/