4D ROOSTERΒΆ
This example shows how to perform iterative cone-beam CT reconstruction using either CPU or GPU resources. It reads its data from disk. The data can be generated by the Generate 4D data example or downloaded from Girder.
#include "rtkFourDROOSTERConeBeamReconstructionFilter.h"
#include "rtkIterationCommands.h"
#include "rtkSignalToInterpolationWeights.h"
#include "rtkReorderProjectionsImageFilter.h"
#include "rtkProjectionsReader.h"
#include "rtkThreeDCircularProjectionGeometryXMLFileReader.h"
#ifdef RTK_USE_CUDA
# include <itkCudaImage.h>
#endif
#include <itkImageFileWriter.h>
int
main(int, char *[])
{
using OutputPixelType = float;
using DVFVectorType = itk::CovariantVector<OutputPixelType, 3>;
#ifdef RTK_USE_CUDA
using VolumeSeriesType = itk::CudaImage<OutputPixelType, 4>;
using ProjectionStackType = itk::CudaImage<OutputPixelType, 3>;
using DVFSequenceImageType = itk::CudaImage<DVFVectorType, VolumeSeriesType::ImageDimension>;
#else
using VolumeSeriesType = itk::Image<OutputPixelType, 4>;
using ProjectionStackType = itk::Image<OutputPixelType, 3>;
using DVFSequenceImageType = itk::Image<DVFVectorType, VolumeSeriesType::ImageDimension>;
#endif
// Generate the input volume series, used as initial estimate by 4D conjugate gradient
auto fourDOrigin = itk::MakePoint(-63., -31., -63., 0.);
auto fourDSpacing = itk::MakeVector(4., 4., 4., 1.);
auto fourDSize = itk::MakeSize(32, 16, 32, 8);
using ConstantFourDSourceType = rtk::ConstantImageSource<VolumeSeriesType>;
auto fourDSource = ConstantFourDSourceType::New();
fourDSource->SetOrigin(fourDOrigin);
fourDSource->SetSpacing(fourDSpacing);
fourDSource->SetSize(fourDSize);
fourDSource->Update();
// Read geometry, projections and signal
rtk::ThreeDCircularProjectionGeometry::Pointer geometry;
TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry("four_d_geometry.xml"));
using ReaderType = rtk::ProjectionsReader<ProjectionStackType>;
auto projectionsReader = ReaderType::New();
std::vector<std::string> fileNames = std::vector<std::string>();
fileNames.push_back("four_d_projections.mha");
projectionsReader->SetFileNames(fileNames);
TRY_AND_EXIT_ON_ITK_EXCEPTION(projectionsReader->Update());
// Re-order geometry and projections
// In the new order, projections with identical phases are packed together
std::vector<double> signal = rtk::ReadSignalFile("four_d_signal.txt");
auto reorder = rtk::ReorderProjectionsImageFilter<ProjectionStackType>::New();
reorder->SetInput(projectionsReader->GetOutput());
reorder->SetInputGeometry(geometry);
reorder->SetInputSignal(signal);
TRY_AND_EXIT_ON_ITK_EXCEPTION(reorder->Update())
// Release the memory holding the stack of original projections
projectionsReader->GetOutput()->ReleaseData();
// Compute the interpolation weights
auto signalToInterpolationWeights = rtk::SignalToInterpolationWeights::New();
signalToInterpolationWeights->SetSignal(reorder->GetOutputSignal());
signalToInterpolationWeights->SetNumberOfReconstructedFrames(fourDSize[3]);
TRY_AND_EXIT_ON_ITK_EXCEPTION(signalToInterpolationWeights->Update())
// Set the forward and back projection filters to be used
using ROOSTERFilterType = rtk::FourDROOSTERConeBeamReconstructionFilter<VolumeSeriesType, ProjectionStackType>;
auto rooster = ROOSTERFilterType::New();
rooster->SetInputVolumeSeries(fourDSource->GetOutput());
rooster->SetCG_iterations(2);
rooster->SetMainLoop_iterations(2);
#ifdef RTK_USE_CUDA
rooster->SetCudaConjugateGradient(true);
rooster->SetUseCudaCyclicDeformation(true);
rooster->SetForwardProjectionFilter(ROOSTERFilterType::FP_CUDARAYCAST);
rooster->SetBackProjectionFilter(ROOSTERFilterType::BP_CUDAVOXELBASED);
#else
rooster->SetForwardProjectionFilter(ROOSTERFilterType::FP_JOSEPH);
rooster->SetBackProjectionFilter(ROOSTERFilterType::BP_VOXELBASED);
#endif
// Set the newly ordered arguments
rooster->SetInputProjectionStack(reorder->GetOutput());
rooster->SetGeometry(reorder->GetOutputGeometry());
rooster->SetWeights(signalToInterpolationWeights->GetOutput());
rooster->SetSignal(reorder->GetOutputSignal());
// For each optional regularization step, set whether or not
// it should be performed, and provide the necessary inputs
// Positivity
rooster->SetPerformPositivity(true);
// Motion mask
rooster->SetPerformMotionMask(false);
// Spatial TV
rooster->SetGammaTVSpace(0.1);
rooster->SetTV_iterations(4);
rooster->SetPerformTVSpatialDenoising(true);
// Spatial wavelets
rooster->SetPerformWaveletsSpatialDenoising(false);
// Temporal TV
rooster->SetGammaTVTime(0.1);
rooster->SetTV_iterations(4);
rooster->SetPerformTVTemporalDenoising(true);
// Temporal L0
rooster->SetPerformL0TemporalDenoising(false);
// Total nuclear variation
rooster->SetPerformTNVDenoising(false);
// Warping
rooster->SetPerformWarping(true);
rooster->SetUseNearestNeighborInterpolationInWarping(false);
DVFSequenceImageType::Pointer dvf;
TRY_AND_EXIT_ON_ITK_EXCEPTION(dvf = itk::ReadImage<DVFSequenceImageType>("four_d_dvf.mha"))
rooster->SetDisplacementField(dvf);
rooster->SetComputeInverseWarpingByConjugateGradient(false);
DVFSequenceImageType::Pointer idvf;
TRY_AND_EXIT_ON_ITK_EXCEPTION(idvf = itk::ReadImage<DVFSequenceImageType>("four_d_idvf.mha"))
rooster->SetInverseDisplacementField(idvf);
auto verboseIterationCommand = rtk::VerboseIterationCommand<ROOSTERFilterType>::New();
rooster->AddObserver(itk::AnyEvent(), verboseIterationCommand);
TRY_AND_EXIT_ON_ITK_EXCEPTION(rooster->Update())
// Write
TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(rooster->GetOutput(), "fourdrooster.mha"));
return EXIT_SUCCESS;
}
import itk
from itk import RTK as rtk
OutputPixelType = itk.F
CpuProjectionStackType = itk.Image[OutputPixelType, 3]
CpuVolumeSeriesType = itk.Image[OutputPixelType, 4]
DVFVectorType = itk.CovariantVector[OutputPixelType, 3]
CpuDVFSequenceImageType = itk.Image[DVFVectorType, 4]
if hasattr(itk, "CudaImage"):
ProjectionStackType = itk.CudaImage[OutputPixelType, 3]
VolumeSeriesType = itk.CudaImage[OutputPixelType, 4]
else:
ProjectionStackType = CpuProjectionStackType
VolumeSeriesType = CpuVolumeSeriesType
# Generate the input volume series, used as initial estimate by 4D conjugate gradient
four_d_source = rtk.constant_image_source(
ttype=[VolumeSeriesType],
origin=[-63.0, -31.0, -63.0, 0.0],
spacing=[4.0, 4.0, 4.0, 1.0],
size=[32, 16, 32, 8],
)
# Read geometry, projections and signal
geometry = rtk.read_geometry("four_d_geometry.xml")
projections_reader = rtk.ProjectionsReader[CpuProjectionStackType].New()
file_names = ["four_d_projections.mha"]
projections_reader.SetFileNames(file_names)
projections_reader.Update()
signal = rtk.read_signal_file("four_d_signal.txt")
# Re-order geometry and projections
# In the new order, projections with identical phases are packed together
reorder = rtk.ReorderProjectionsImageFilter[
ProjectionStackType, ProjectionStackType
].New()
if hasattr(itk, "CudaImage"):
reorder.SetInput(itk.cuda_image_from_image(projections_reader.GetOutput()))
else:
reorder.SetInput(projections_reader.GetOutput())
reorder.SetInputGeometry(geometry)
reorder.SetInputSignal(signal)
reorder.Update()
# Release the memory holding the stack of original projections
projections_reader.GetOutput().ReleaseData()
# Compute the interpolation weights
signal_to_interpolation_weights = rtk.SignalToInterpolationWeights.New()
signal_to_interpolation_weights.SetSignal(reorder.GetOutputSignal())
signal_to_interpolation_weights.SetNumberOfReconstructedFrames(
four_d_source.GetLargestPossibleRegion().GetSize()[3]
)
signal_to_interpolation_weights.Update()
# Set the forward and back projection filters to be used
ROOSTERFilterType = rtk.FourDROOSTERConeBeamReconstructionFilter[
VolumeSeriesType, ProjectionStackType
]
rooster = ROOSTERFilterType.New()
rooster.SetInputVolumeSeries(four_d_source)
rooster.SetCG_iterations(2)
rooster.SetMainLoop_iterations(2)
if hasattr(itk, "CudaImage"):
rooster.SetCudaConjugateGradient(True)
rooster.SetUseCudaCyclicDeformation(True)
rooster.SetForwardProjectionFilter(
ROOSTERFilterType.ForwardProjectionType_FP_CUDARAYCAST
)
rooster.SetBackProjectionFilter(
ROOSTERFilterType.BackProjectionType_BP_CUDAVOXELBASED
)
else:
rooster.SetForwardProjectionFilter(
ROOSTERFilterType.ForwardProjectionType_FP_JOSEPH
)
rooster.SetBackProjectionFilter(ROOSTERFilterType.BackProjectionType_BP_VOXELBASED)
# Set the newly ordered arguments
rooster.SetInputProjectionStack(reorder.GetOutput())
rooster.SetGeometry(reorder.GetOutputGeometry())
rooster.SetWeights(signal_to_interpolation_weights.GetOutput())
rooster.SetSignal(reorder.GetOutputSignal())
# For each optional regularization step, set whether or not
# it should be performed, and provide the necessary inputs
# Positivity
rooster.SetPerformPositivity(True)
# Motion mask
rooster.SetPerformMotionMask(False)
# Spatial TV
rooster.SetGammaTVSpace(0.1)
rooster.SetTV_iterations(4)
rooster.SetPerformTVSpatialDenoising(True)
# Spatial wavelets
rooster.SetPerformWaveletsSpatialDenoising(False)
# Temporal TV
rooster.SetGammaTVTime(0.1)
rooster.SetTV_iterations(4)
rooster.SetPerformTVTemporalDenoising(True)
# Temporal L0
rooster.SetPerformL0TemporalDenoising(False)
# Total nuclear variation
rooster.SetPerformTNVDenoising(False)
# Warping
rooster.SetPerformWarping(True)
rooster.SetUseNearestNeighborInterpolationInWarping(False)
dvf_reader = itk.ImageFileReader[CpuDVFSequenceImageType].New()
dvf_reader.SetFileName("four_d_dvf.mha")
dvf = dvf_reader.GetOutput()
if hasattr(itk, "CudaImage"):
dvf = itk.cuda_image_from_image(dvf)
rooster.SetDisplacementField(dvf)
rooster.SetComputeInverseWarpingByConjugateGradient(False)
idvf_reader = itk.ImageFileReader[CpuDVFSequenceImageType].New()
idvf_reader.SetFileName("four_d_idvf.mha")
idvf = idvf_reader.GetOutput()
if hasattr(itk, "CudaImage"):
idvf = itk.cuda_image_from_image(idvf)
rooster.SetInverseDisplacementField(idvf)
class VerboseIterationCommand:
def __init__(self):
self.count = 0
def callback(self):
self.count += 1
print(f"Iteration {self.count}", end="\r")
class VerboseEndCommand:
def callback(self):
print("")
cmd = VerboseIterationCommand()
rooster.AddObserver(itk.IterationEvent(), cmd.callback)
cmd_end = VerboseEndCommand()
rooster.AddObserver(itk.EndEvent(), cmd_end.callback)
rooster.Update()
# Write
writer = itk.ImageFileWriter[CpuVolumeSeriesType].New()
writer.SetFileName("fourdrooster.mha")
writer.SetInput(rooster.GetOutput())
writer.Update()
