package edu.stanford.rsl.tutorial.fan.redundancy; import ij.ImageJ; import edu.stanford.rsl.conrad.data.numeric.Grid2D; import edu.stanford.rsl.conrad.data.numeric.Grid3D; import edu.stanford.rsl.conrad.data.numeric.NumericPointwiseOperators; public class BinaryWeights_erodeByOne extends Grid2D { private final double focalLength; private final double maxT; private final double deltaT, dLambda; private final int maxTIndex, maxLambdaIndex; private Grid2D binaryMask; public BinaryWeights_erodeByOne(final double focalLength, final double maxT, final double deltaT, double maxLambda, double dLambda) { // Call constructor from superclass super(((int) Math.round(maxT / deltaT)), ((int) Math.round(maxLambda / dLambda) + 1)); // Initialize parameters this.focalLength = focalLength; this.maxT = maxT; this.deltaT = deltaT; this.dLambda = dLambda; this.maxLambdaIndex = (int) (Math.round(maxLambda / dLambda)) + 1; this.maxTIndex = (int) Math.round(maxT / deltaT); binaryMask = new Grid2D(this); createMask(); createWeights(); // Correct for scaling due to varying angle lambda NumericPointwiseOperators.multiplyBy(this, (float) (maxLambda / (Math.PI))); } private void createMask() { double lambda, delta; // iterate over the detector elements for (int t = 0; t < maxTIndex; ++t) { // compute delta of the current ray (detector element) delta = Math.atan((t * deltaT - maxT / 2.d + 0.5 * deltaT) / focalLength); // iterate over the projection angles for (int b = 0; b < maxLambdaIndex; ++b) { // compute the current lambda angle lambda = b * dLambda; // Default weight is 1 binaryMask.setAtIndex(maxTIndex - t - 1, b, 1.0f); this.setAtIndex(maxTIndex - t - 1, b, 1.0f); // First case: Handles weights for redundancies at the end of // the scan // Set weights to zero (Simulates the collimator) if (lambda >= (Math.PI + 2 * delta)) { binaryMask.setAtIndex(maxTIndex - t - 1, b, 0.f); this.setAtIndex(maxTIndex - t - 1, b, 0.f); } } } } private void createWeights() { Grid2D tmp = new Grid2D(this); NumericPointwiseOperators.fill(tmp, -100.f); // basically an dilation step! // iterate over the projection angles for (int b = 0; b < maxLambdaIndex; ++b) { // iterate over the detector elements for (int t = 0; t < maxTIndex; ++t) { if (t > 0) { if (this.getAtIndex(t, b) == 1 || this.getAtIndex(t-1, b) == 1) { tmp.setAtIndex(t, b, 1.f); } else { tmp.setAtIndex(t, b, 0.f); } } if (t == 0) { tmp.setAtIndex(t, b, this.getAtIndex(t, b)); } } } // iterate over the projection angles for (int b = 0; b < maxLambdaIndex; ++b) { // iterate over the detector elements for (int t = 0; t < maxTIndex; ++t) { this.setAtIndex(t, b, tmp.getAtIndex(t, b)); } } } public Grid2D getBinaryMask() { return binaryMask; } public void applyToGrid(Grid2D sino) { NumericPointwiseOperators.multiplyBy(sino, this); } public static void main(String[] args) { // fan beam bp parameters double maxT = 400; double deltaT = 1.d; // set focal length according to the fan angle double focalLength = (maxT / 2.0 - 0.5) / Math.tan(30.0 * Math.PI / 180.0); new ImageJ(); Grid3D g = new Grid3D((int)maxT, 181, 360); for (int i = 0; i < 360; ++i) { double maxBeta = (double)(i+1) * Math.PI * 2.0 / 360.0; double deltaBeta = maxBeta / 180; BinaryWeights p = new BinaryWeights(focalLength, maxT, deltaT, maxBeta, deltaBeta); g.setSubGrid(i, p); //g.setSliceLabel("MaxBeta: " + Double.toString(maxBeta*180/Math.PI), i+1); } } g.show(); double maxBeta = Math.PI; double deltaBeta = maxBeta / 180; BinaryWeights_erodeByOne p = new BinaryWeights_erodeByOne(focalLength, maxT, deltaT, maxBeta, deltaBeta); p.show("Weights"); p.getBinaryMask().show("Mask"); } } /* * Copyright (C) 2010-2014 Andreas Maier * CONRAD is developed as an Open Source project under the GNU General Public License (GPL). */