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). */