Project, draw and reconstruct geometric phantomΒΆ
GeometricPhantom demonstrates how to create 2D projections of a Forbild phantom, draw a reference 3D image and reconstruct from the projections with FDK. You can find how to create your own phantom file in the phantom definition page.
This example uses the Thorax Forbild phantom, available in the dedicated GitHub repository for Forbild phantom files compatible with RTK.
#include "rtkDrawGeometricPhantomImageFilter.h"
#include "rtkConstantImageSource.h"
#include "rtkProjectGeometricPhantomImageFilter.h"
#include "rtkFDKConeBeamReconstructionFilter.h"
#include "rtkThreeDCircularProjectionGeometryXMLFileWriter.h"
int
main()
{
constexpr unsigned int Dimension = 3;
using OutputPixelType = float;
using OutputImageType = itk::Image<OutputPixelType, Dimension>;
constexpr unsigned int numberOfProjections = 180;
constexpr double angularArc = 360.;
constexpr unsigned int sid = 600; // source to isocenter distance
constexpr unsigned int sdd = 1200; // source to detector distance
constexpr double scale = 2.;
const std::string configFileName = "Thorax";
itk::Matrix<double, Dimension, Dimension> rotation;
rotation[0][0] = 1.;
rotation[1][2] = 1.;
rotation[2][1] = 1.;
// Set up the geometry for the projections
auto geometry = rtk::ThreeDCircularProjectionGeometry::New();
for (unsigned int x = 0; x < numberOfProjections; x++)
{
geometry->AddProjection(sid, sdd, x * angularArc / numberOfProjections);
}
rtk::WriteGeometry(geometry, "geometry.xml");
// Create a constant image source for the tomography
auto tomographySource = rtk::ConstantImageSource<OutputImageType>::New();
tomographySource->SetOrigin(itk::MakePoint(-63.5, -63.5, -63.5));
tomographySource->SetSize(itk::MakeSize(128, 128, 128));
// Create a constant image source for the projections
auto projectionsSource = rtk::ConstantImageSource<OutputImageType>::New();
projectionsSource->SetOrigin(itk::MakePoint(-127., -127., -127.));
projectionsSource->SetSpacing(itk::MakeVector(2., 2., 2.));
projectionsSource->SetSize(itk::MakeSize(128, 128, numberOfProjections));
// Project the geometric phantom image
auto pgp = rtk::ProjectGeometricPhantomImageFilter<OutputImageType, OutputImageType>::New();
pgp->SetInput(projectionsSource->GetOutput());
pgp->SetGeometry(geometry);
pgp->SetPhantomScale(scale);
pgp->SetRotationMatrix(rotation);
pgp->SetConfigFile(configFileName);
pgp->SetIsForbildConfigFile(true);
TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(pgp->GetOutput(), "projections.mha"));
// Draw the geometric phantom image
auto dgp = rtk::DrawGeometricPhantomImageFilter<OutputImageType, OutputImageType>::New();
dgp->SetInput(tomographySource->GetOutput());
dgp->SetPhantomScale(scale);
dgp->SetRotationMatrix(rotation);
dgp->SetConfigFile(configFileName);
dgp->SetIsForbildConfigFile(true);
TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(dgp->GetOutput(), "ref.mha"));
// Perform FDK reconstruction filtering
auto feldkamp = rtk::FDKConeBeamReconstructionFilter<OutputImageType>::New();
feldkamp->SetInput(0, tomographySource->GetOutput());
feldkamp->SetInput(1, pgp->GetOutput());
feldkamp->SetGeometry(geometry);
TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(feldkamp->GetOutput(), "fdk.mha"));
return EXIT_SUCCESS;
}
import itk
from itk import RTK as rtk
Dimension = 3
OutputPixelType = itk.F
OutputImageType = itk.Image[OutputPixelType, Dimension]
numberOfProjections = 180
angularArc = 360.0
sid = 600
sdd = 1200
scale = 2.0
config_file_name = "Thorax"
rotation = itk.matrix_from_array([[1.0, 0.0, 0.0], [0.0, 0.0, -1.0], [0.0, 1.0, 0.0]])
# Set up the geometry for the projections
geometry = rtk.ThreeDCircularProjectionGeometry.New()
for x in range(0, numberOfProjections):
angle = x * angularArc / numberOfProjections
geometry.AddProjection(sid, sdd, angle)
rtk.write_geometry(geometry, "geometry.xml")
# Project the geometric phantom image
projections_source = rtk.constant_image_source(
ttype=[OutputImageType],
origin=[-127.0, -127.0, 0.0],
size=[128, 128, numberOfProjections],
spacing=[2.0] * 3,
)
pgp = rtk.project_geometric_phantom_image_filter(
projections_source,
geometry=geometry,
phantom_scale=scale,
rotation_matrix=rotation,
config_file=config_file_name,
is_forbild_config_file=True,
)
itk.imwrite(pgp, "projections.mha")
# Draw the geometric phantom image
ref_source = rtk.constant_image_source(
ttype=[OutputImageType], origin=[-63.5] * 3, size=[128] * 3
)
dgp = rtk.draw_geometric_phantom_image_filter(
ref_source,
phantom_scale=scale,
rotation_matrix=rotation,
config_file=config_file_name,
is_forbild_config_file=True,
)
itk.imwrite(dgp, "ref.mha")
# Perform FDK reconstruction filtering
fdk_source = rtk.constant_image_source(
ttype=[OutputImageType],
origin=[-63.5] * 3,
size=[128] * 3,
spacing=[1.0] * 3,
constant=0.0,
)
feldkamp = rtk.fdk_cone_beam_reconstruction_filter(fdk_source, pgp, geometry=geometry)
itk.imwrite(feldkamp, "fdk.mha")
The results displayed with rtkshowgeometry are: