package edu.stanford.rsl.tutorial.truncation;
import ij.ImageJ;
import edu.stanford.rsl.conrad.data.numeric.Grid2D;
import edu.stanford.rsl.conrad.data.numeric.NumericPointwiseOperators;
import edu.stanford.rsl.tutorial.filters.RamLakKernel;
import edu.stanford.rsl.tutorial.parallel.ParallelBackprojector2D;
import edu.stanford.rsl.tutorial.parallel.ParallelProjector2D;
import edu.stanford.rsl.tutorial.phantoms.UniformCircleGrid2D;
/**
* Example Class to show the linearity of the truncation artifacts.
* If we split the projection image into the truncated and the non-truncated part,
* we can see that the filtered backprojection of both combined in image space
* gives a correct reconstruction. Furthermore, the forward projection of the artifact
* image gives nearly 0 in the area where the truncation took place. Note that this
* is only perfect in continuous domain. With discrete sampling a slight residual artifact
* remains.
*
* @author akmaier
*
*/
public class WaterCylinderFBPExample {
public static void main (String [] args){
new ImageJ();
int numS = 200;
double deltaS = 200.0 / 201.0;
int numTheta = 360;
int truncationSize = 50;
UniformCircleGrid2D cylinder = new UniformCircleGrid2D(numS, numS);
ParallelProjector2D projector2d = new ParallelProjector2D(Math.PI, Math.PI/(numTheta-1), numS, deltaS);
Grid2D sinogram = projector2d.projectRayDrivenCL(cylinder);
sinogram.show("Cylinder Sinogram");
Grid2D truncatedSinogram = new Grid2D(sinogram);
for (int i=0; i < numTheta; i++) {
for (int j = 0; j < truncationSize; j++){
truncatedSinogram.setAtIndex(i, j, 0);
}
for (int j = numS - truncationSize+1; j < numS; j++){
truncatedSinogram.setAtIndex(i, j, 0);
}
}
truncatedSinogram.show("Truncated Image");
Grid2D artifactSinogram = new Grid2D(sinogram);
NumericPointwiseOperators.multiplyBy(sinogram, -1);
artifactSinogram = (Grid2D)NumericPointwiseOperators.addedBy(truncatedSinogram, sinogram);
NumericPointwiseOperators.multiplyBy(sinogram, -1);
NumericPointwiseOperators.multiplyBy(artifactSinogram, -1);
artifactSinogram.show("Artifact Image");
RamLakKernel ramLakFilter = new RamLakKernel((int) (numS / deltaS), deltaS);
for (int theta = 0; theta < sinogram.getSize()[0]; ++theta) {
ramLakFilter.applyToGrid(artifactSinogram.getSubGrid(theta));
ramLakFilter.applyToGrid(truncatedSinogram.getSubGrid(theta));
}
ParallelBackprojector2D backprojector2d = new ParallelBackprojector2D(numS, numS, 1, 1);
Grid2D truncatedImage = backprojector2d.backprojectPixelDriven(truncatedSinogram);
truncatedImage.show("Truncated Image");
Grid2D artifactImage = backprojector2d.backprojectPixelDriven(artifactSinogram);
artifactImage.show("Artifact Image");
cylinder = (UniformCircleGrid2D)NumericPointwiseOperators.addedBy(artifactImage, truncatedImage);
cylinder.show("Combination of both");
Grid2D FPofArtifactImage = projector2d.projectRayDrivenCL(artifactImage);
FPofArtifactImage.show("FP of Artifact Image");
Grid2D FPofTruncImage = projector2d.projectRayDrivenCL(truncatedImage);
FPofTruncImage.show("FP of Truncation Image");
Grid2D combinedSinogram = new Grid2D(sinogram);
combinedSinogram = (Grid2D)NumericPointwiseOperators.addedBy(FPofArtifactImage, FPofTruncImage);
combinedSinogram.show("Combination of FP");
}
}
/*
* Copyright (C) 2010-2014 Andreas Maier
* CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
*/