/** * constructor to create a view * * @param projection: projection image as Grid2D * @param radon: radon transformed and derived image as Grid2D * @param projMatrix: projection matrix as Projection */ public View(Grid2D projection, Grid2D radon, Projection projMatrix) { // Initialize center matrix // CENTER = new SimpleMatrix(3, 4); CENTER.setDiagValue(new SimpleVector(1.0, 1.0, 1.0)); // get data out of projection // this.projectionWidth = projection.getWidth(); this.projectionHeight = projection.getHeight(); // get data out of radon transformed image // this.radonWidth = radon.getWidth(); this.radonHeight = radon.getHeight(); this.projectionDiag = Math.sqrt(projectionWidth * projectionWidth + projectionHeight * projectionHeight); this.lineIncrement = radonWidth / projectionDiag; this.angleIncrement = radonHeight / Math.PI; // store radon transformed image // this.radon = radon; // get projection matrix P (3x4) // this.P = SimpleOperators.multiplyMatrixProd(projMatrix.getK(), CENTER); this.P = SimpleOperators.multiplyMatrixProd(this.P, projMatrix.getRt()); // get source position C (nullspace of the projection) // DecompositionSVD decoP = new DecompositionSVD(this.P); this.C = decoP.getV().getCol(3); // normalize source vectors by last component // // it is important that the last component is positive to have a positive center // as it is defined in oriented projective geometry this.C = this.C.dividedBy(this.C.getElement(3)); }
/** * Creates the meshes of one single phase and adds it to the ArrayList of 4D meshes. * * @param phase * @param parameters * @param info * @param splines */ private void createPhantom( int phase, double[] parameters, CONRADCardiacModelConfig info, ArrayList<Mesh4D> splines) { String pcaFile = heartBase + "\\CardiacModel\\phase_" + phase + ".ccm"; ActiveShapeModel asm = new ActiveShapeModel(pcaFile); Mesh allComp = asm.getModel(parameters); if (phase == 0) { for (int i = 0; i < info.numAnatComp; i++) { splines.add(new Mesh4D()); } } int count = 0; for (heartComponents hc : heartComponents.values()) { Mesh comp = new Mesh(); SimpleMatrix pts = allComp .getPoints() .getSubMatrix(info.vertexOffs[count], 0, hc.getNumVertices(), info.vertexDim); // rotate and translate points for (int i = 0; i < pts.getRows(); i++) { SimpleVector row = SimpleOperators.multiply(rot, pts.getRow(i)); row.add(trans); pts.setRowValue(i, row); } comp.setPoints(pts); comp.setConnectivity( allComp .getConnectivity() .getSubMatrix(info.triangleOffs[count], 0, hc.getNumTriangles(), 3)); splines.get(count).addMesh(comp); count++; } }
/** * method to calculate a mapping K from two source positions C0, C1 to a plane C0 (C1) is the * source position from the first (second) view */ public void createMappingToEpipolarPlane() { // set up source matrices // SimpleVector C0 = this.view1.C; SimpleVector C1 = this.view2.C; // compute Pluecker coordinates // double L01 = C0.getElement(0) * C1.getElement(1) - C0.getElement(1) * C1.getElement(0); double L02 = C0.getElement(0) * C1.getElement(2) - C0.getElement(2) * C1.getElement(0); double L03 = C0.getElement(0) * C1.getElement(3) - C0.getElement(3) * C1.getElement(0); double L12 = C0.getElement(1) * C1.getElement(2) - C0.getElement(2) * C1.getElement(1); double L13 = C0.getElement(1) * C1.getElement(3) - C0.getElement(3) * C1.getElement(1); double L23 = C0.getElement(2) * C1.getElement(3) - C0.getElement(3) * C1.getElement(2); // construct B (6x1) // SimpleVector B = new SimpleVector(L01, L02, L03, L12, L13, L23); // compute infinity point in direction of B // SimpleVector N = new SimpleVector(-L03, -L13, -L23, 0); // compute plane E0 containing B and X0=(0,0,0,1) // SimpleVector E0 = SimpleOperators.getPlueckerJoin(B, new SimpleVector(0, 0, 0, 1)); // find othonormal basis from plane normals // // (vectors are of 3x1) SimpleVector a2 = new SimpleVector(E0.getElement(0), E0.getElement(1), E0.getElement(2)); SimpleVector a3 = new SimpleVector(N.getElement(0), N.getElement(1), N.getElement(2)); // set vectors to unit length a2.normalizeL2(); a3.normalizeL2(); // calculate cross product to get the last basis vector // SimpleVector a1 = General.crossProduct(a2, a3).negated(); // (a1 is already of unit length -> no normalization needed) // set up assembly matrix A (4x3) // SimpleMatrix A = new SimpleMatrix(4, 3); A.setSubColValue(0, 0, a1); A.setSubColValue(0, 1, a2); A.setSubColValue(0, 2, C0); // store mapping matrix K (4x3 = 4x4 * 4x3) // this.K = SimpleOperators.multiplyMatrixProd(SimpleOperators.getPlueckerMatrixDual(B), A); }
@Override public void workOnSlice(int sliceNumber) { PrioritizableScene phantomScene = phantom; if (phantom instanceof AnalyticPhantom4D) { AnalyticPhantom4D scene4D = (AnalyticPhantom4D) phantom; phantomScene = scene4D.getScene(((double) sliceNumber) / trajectory.getProjectionStackSize()); String disableAutoCenterBoolean = Configuration.getGlobalConfiguration() .getRegistryEntry(RegKeys.DISABLE_CENTERING_4DPHANTOM_PROJECTION_RENDERING); boolean disableAutoCenter = false; if (disableAutoCenterBoolean != null) { disableAutoCenter = Boolean.parseBoolean(disableAutoCenterBoolean); } Translation centerTranslation = new Translation(new SimpleVector(0, 0, 0)); if (!disableAutoCenter) { SimpleVector center = SimpleOperators.add( phantom.getMax().getAbstractVector(), phantom.getMin().getAbstractVector()) .dividedBy(2); centerTranslation = new Translation(center.negated()); } for (PhysicalObject o : phantomScene) { o.getShape().applyTransform(centerTranslation); // System.out.println(o.getShape().getMax() + " " + o.getShape().getMin()); // Translate a part of XCAT to the center of source & detector for 2D projection (e.g. knee // at the center of the 2d projection) String translationString = Configuration.getGlobalConfiguration() .getRegistryEntry(RegKeys.GLOBAL_TRANSLATION_4DPHANTOM_PROJECTION_RENDERING); if (translationString != null) { // Center b/w RKJC & LKJC: -292.6426 211.7856 440.7783 (subj 5, static60),-401.1700 // 165.9885 478.5600 (subj 2, static60) // XCAT Center by min & max: -177.73999504606988, 179.8512744259873, 312.19713254613583 // translationVector = (XCAT Center by min & max) - (Center b/w RKJC & LKJC)=> // 114.9026, -31.9343, -128.5811 (subj5), 120, 3, -110(subj2) Try 114.0568 2.4778 // -106.2550 String[] values = translationString.split(", "); SimpleVector translationVector = new SimpleVector( Double.parseDouble(values[0]), Double.parseDouble(values[1]), Double.parseDouble(values[2])); Translation translationToRotationCenter = new Translation(translationVector); o.getShape().applyTransform(translationToRotationCenter); } } // System.out.println(phantomScene.getMax() + " " + phantomScene.getMin()); } Grid2D slice = raytraceScene(phantomScene, trajectory.getProjectionMatrix(sliceNumber)); this.imageBuffer.add(slice, sliceNumber); }
private double[] compute2DCoordinates(double[] point3D, SimpleMatrix pMatrix) { // Compute coordinates in projection data. SimpleVector homogeneousPoint = SimpleOperators.multiply(pMatrix, new SimpleVector(point3D[0], point3D[1], point3D[2], 1)); // Transform to 2D coordinates double coordU = homogeneousPoint.getElement(0) / homogeneousPoint.getElement(2); double coordV = homogeneousPoint.getElement(1) / homogeneousPoint.getElement(2); // double pxlSize = config.getGeometry().getPixelDimensionX(); return new double[] {coordU, coordV}; }
/** * method to compute the epipolar line integrals that state the epipolar consistency conditions by * comparison of two views * * @param kappa_RAD: angle of epipolar plane * @return double[] array containing two values (one for each view's line integral) */ public double[] computeEpipolarLineIntegrals(double kappa_RAD) { // compute points on unit-circle (3x1) // SimpleVector x_kappa = new SimpleVector(Math.cos(kappa_RAD), Math.sin(kappa_RAD), 1); // compute epipolar plane E_kappa (4x1 = 4x3 * 3x1) // SimpleVector E_kappa = SimpleOperators.multiply(this.K, x_kappa); // compute line integral out of derived radon transform // double value1 = view1.getValueByAlphaAndT(E_kappa); double value2 = view2.getValueByAlphaAndT(E_kappa); // both values are returned // return new double[] {value1, value2}; }
/** * Method to calculate alpha and t as indices from the radon transformed image of a view. * Neccessary are the inverse projection image (as SimpleMatrix) and the epipolar plane E * * @param E: epipolar plane E as SimpleVector (4 entries) * @return: line integral value as double */ private double getValueByAlphaAndT(SimpleVector E) { // compute corresponding epipolar lines // // (epipolar lines are of 3x1 = 3x4 * 4x1) SimpleVector l_kappa = SimpleOperators.multiply(this.P_Inverse.transposed(), E); // init the coordinate shift // int t_u = this.projectionWidth / 2; int t_v = this.projectionHeight / 2; // compute angle alpha and distance to origin t // double l0 = l_kappa.getElement(0); double l1 = l_kappa.getElement(1); double l2 = l_kappa.getElement(2); double alpha_kappa_RAD = Math.atan2(-l0, l1) + Math.PI / 2; double t_kappa = -l2 / Math.sqrt(l0 * l0 + l1 * l1); // correct the coordinate shift // t_kappa -= t_u * Math.cos(alpha_kappa_RAD) + t_v * Math.sin(alpha_kappa_RAD); // correct some alpha falling out of the radon window // if (alpha_kappa_RAD < 0) { alpha_kappa_RAD *= -1.0; } else if (alpha_kappa_RAD > Math.PI) { alpha_kappa_RAD = 2.0 * Math.PI - alpha_kappa_RAD; } // write back to l_kappa // l_kappa.setElementValue(0, Math.cos(alpha_kappa_RAD)); l_kappa.setElementValue(1, Math.sin(alpha_kappa_RAD)); l_kappa.setElementValue(2, -t_kappa); // calculate x and y coordinates for derived radon transformed image // double x = t_kappa * this.lineIncrement + 0.5 * this.radonWidth; double y = alpha_kappa_RAD * this.angleIncrement; // get intensity value out of radon transformed image // // (interpolation needed) return InterpolationOperators.interpolateLinear(this.radon, x, y); }
public Grid2D raytraceScene(PrioritizableScene phantomScene, Projection projection) { Trajectory geom = Configuration.getGlobalConfiguration().getGeometry(); // Grid2D slice = new Grid2D(geom.getDetectorWidth(), geom.getDetectorHeight()); Grid2D slice = detector.createDetectorGrid(geom.getDetectorWidth(), geom.getDetectorHeight(), projection); rayTracer.setScene(phantomScene); // Second rule of optimization is: Optimize later. PointND raySource = new PointND(0, 0, 0); raySource.setCoordinates(projection.computeCameraCenter()); StraightLine castLine = new StraightLine(raySource, new SimpleVector(0, 0, 0)); SimpleVector centerPixDir = null; if (accurate) { centerPixDir = projection.computePrincipalAxis(); } // SimpleVector prinpoint = trajectory.getProjectionMatrix(sliceNumber).getPrincipalPoint(); double xcorr = 0; // trajectory.getDetectorWidth()/2 - prinpoint.getElement(0); double ycorr = 0; // trajectory.getDetectorHeight()/2 - prinpoint.getElement(1); double length = trajectory.getSourceToDetectorDistance(); Edge environmentEdge = new Edge(new PointND(0), new PointND(length)); ArrayList<PhysicalObject> fallBackBackground = new ArrayList<PhysicalObject>(1); SimpleVector pixel = new SimpleVector(0, 0); boolean negate = false; for (int y = 0; y < trajectory.getDetectorHeight(); y++) { for (int x = 0; x < trajectory.getDetectorWidth(); x++) { pixel.setElementValue(0, x - xcorr); pixel.setElementValue(1, y - ycorr); SimpleVector dir = projection.computeRayDirection(pixel); if ((y == 0) && (x == 0)) { // Check that ray direction is towards origin. double max = 0; int index = 0; for (int i = 0; i < 3; i++) { if (Math.abs(dir.getElement(i)) > max) { max = Math.abs(dir.getElement(i)); index = i; } } double t = -raySource.get(index) / dir.getElement(index); if (t < 0) negate = true; } if (negate) dir.negate(); castLine.setDirection(dir); ArrayList<PhysicalObject> segments = rayTracer.castRay(castLine); if (accurate) { double dirCosine = SimpleOperators.multiplyInnerProd(centerPixDir, dir); length = trajectory.getSourceToDetectorDistance() / dirCosine; } if (segments == null) { fallBackBackground.clear(); segments = fallBackBackground; } else { if (accurate) { environmentEdge = new Edge(new PointND(0), new PointND(length - getTotalSegmentsLength(segments))); } } environment.setShape(environmentEdge); segments.add(environment); /* old code: double integral = absorptionModel.evaluateLineIntegral(segments); slice.putPixelValue(x, y, integral); */ detector.writeToDetector(slice, x, y, segments); } } return slice; }
public void setTrajectory( int numProjectionMatrices, double sourceToAxisDistance, double averageAngularIncrement, double detectorOffsetX, double detectorOffsetY, CameraAxisDirection uDirection, CameraAxisDirection vDirection, SimpleVector rotationAxis, PointND rotationCenter, double angleFirstProjection) { this.projectionMatrices = new Projection[numProjectionMatrices]; this.primaryAngles = new double[numProjectionMatrices]; this.numProjectionMatrices = numProjectionMatrices; this.sourceToAxisDistance = sourceToAxisDistance; this.averageAngularIncrement = averageAngularIncrement; this.detectorOffsetU = detectorOffsetX; this.detectorOffsetV = detectorOffsetY; double cosPhi = Math.cos(General.toRadians(angleFirstProjection)); double sinPhi = Math.sin(General.toRadians(angleFirstProjection)); SimpleMatrix rotMat = new SimpleMatrix(3, 3); rotMat.setElementValue(0, 0, cosPhi); rotMat.setElementValue(0, 1, sinPhi); rotMat.setElementValue(1, 0, -sinPhi); rotMat.setElementValue(1, 1, cosPhi); rotMat.setElementValue(2, 2, 1); SimpleVector centerToCameraIdealAtInitialAngle = SimpleOperators.multiply(rotMat, new SimpleVector(sourceToAxisDistance, 0, 0)); Plane3D trajPlane = new Plane3D( rotationAxis, SimpleOperators.multiplyInnerProd(rotationAxis, rotationCenter.getAbstractVector())); double distToPlane = trajPlane.computeDistance(new PointND(centerToCameraIdealAtInitialAngle)); SimpleVector centerToCameraDir = SimpleOperators.subtract( SimpleOperators.add( rotationAxis.multipliedBy(-1 * distToPlane), centerToCameraIdealAtInitialAngle), rotationCenter.getAbstractVector()); centerToCameraDir.divideBy(centerToCameraDir.normL2()); SimpleVector centerToCameraInitialInPlane = centerToCameraDir.multipliedBy(sourceToAxisDistance); for (int i = 0; i < numProjectionMatrices; i++) { primaryAngles[i] = i * averageAngularIncrement + angleFirstProjection; // System.out.println(primaryAngles[i] + " " + averageAngularIncrement + " " + // this.reconDimensions[0] + " " + this.reconDimensions[1]); projectionMatrices[i] = new Projection(); double rotationAngle = General.toRadians(primaryAngles[i]); projectionMatrices[i].setRtFromCircularTrajectory( rotationCenter.getAbstractVector(), rotationAxis, sourceToAxisDistance, centerToCameraInitialInPlane, uDirection, vDirection, rotationAngle); SimpleVector spacingUV = new SimpleVector(pixelDimensionX, pixelDimensionY); SimpleVector sizeUV = new SimpleVector(detectorWidth, detectorHeight); SimpleVector offset = new SimpleVector(detectorOffsetX, detectorOffsetY); projectionMatrices[i].setKFromDistancesSpacingsSizeOffset( sourceToDetectorDistance, spacingUV, sizeUV, offset, 1.0, 0); } this.projectionStackSize = numProjectionMatrices; // System.out.println("Defined geometry with SDD " +sourceToDetectorDistance); }
private void visualizeKleinNishina(double energyJoule) { int gridWidth = 600; int gridHeight = 500; double maxAngle = 360; Grid2D grid = new Grid2D(gridWidth, gridHeight); FloatProcessor fp = new FloatProcessor(grid.getWidth(), grid.getHeight()); fp.setPixels(grid.getBuffer()); // find max value double max = 0; for (int i = 0; i < maxAngle; ++i) { double value = XRayTracerSampling.comptonAngleProbability(energyJoule, Math.toRadians(i)); if (value > max) { max = value; } } fp.drawLine((int) 0, grid.getHeight() / 2, grid.getWidth(), grid.getHeight() / 2); fp.drawLine(grid.getWidth() / 2, 0, grid.getWidth() / 2, grid.getHeight()); fp.drawOval((grid.getWidth() - grid.getHeight()) / 2, 0, grid.getHeight(), grid.getHeight()); double lastX = 0; double lastY = 0; // draw angle distribution for (int i = 0; i < maxAngle; ++i) { double value = XRayTracerSampling.comptonAngleProbability(energyJoule, Math.toRadians(i)); SimpleMatrix m = Rotations.createBasicZRotationMatrix(Math.toRadians(i)); SimpleVector v = new SimpleVector(((value / max) * ((double) grid.getHeight() / 2.d)), 0, 0); SimpleVector r = SimpleOperators.multiply(m, v); double x = grid.getWidth() / 2 + r.getElement(0); double y = grid.getHeight() / 2 + r.getElement(1); if (i > 0) fp.drawLine((int) lastX, (int) lastY, (int) x, (int) y); lastX = x; lastY = y; } grid.show( "Normalized Klein-Nishina cross-section as a function of scatter angle (" + energyJoule / eV + "eV)"); grid = new Grid2D(gridWidth, gridHeight); fp = new FloatProcessor(grid.getWidth(), grid.getHeight()); fp.setPixels(grid.getBuffer()); // draw histogram with rejection sampling of the klein-nishina // distribution int[] angles = new int[grid.getWidth()]; for (int i = 0; i < numSamples; ++i) { double angle = Math.toDegrees(sampler.getComptonAngleTheta(energyJoule)); int pos = (int) (angle * grid.getWidth() / 360.d); angles[pos] += 1; } double max2 = 0; for (int i = 0; i < angles.length; ++i) { if (angles[i] > max2) { max2 = angles[i]; } } for (int i = 0; i < angles.length; ++i) { double x = i; double y = ((angles[i]) * (grid.getHeight() / (max2))); fp.drawLine((int) x, (int) grid.getHeight(), (int) x, grid.getHeight() - (int) y); } // draw klein-nishina probability function lastX = 0; lastY = 0; for (int i = 0; i < maxAngle; ++i) { double value = XRayTracerSampling.comptonAngleProbability(energyJoule, Math.toRadians(i)); double x = (i * (grid.getWidth() / 360.f)); double y = grid.getHeight() - ((value) * (grid.getHeight() / (max))); fp.drawLine((int) lastX, (int) lastY, (int) x, (int) y); lastX = x; lastY = y; } grid.show( "Energy: " + energyJoule / eV + "eV; x-axis: angle[0-360]; y-axis: probability[0-max]"); }