Generate 4D dataΒΆ
This example shows how to generate a full set of input data to run 4D examples. It contains:
a moving phantom, made of two ellipsoids, one of which moves along the first dimension
a geometry
a phase signal
the set of projections of the moving phantom
a deformation vector field describing the motion of the moving ellipsoid
the corresponding inverse deformation vector field
You can also skip this part and download the data.
#include <string>
#include <fstream>
#include <itkImage.h>
#include <itkCovariantVector.h>
#include <itkPasteImageFilter.h>
#include <itkJoinSeriesImageFilter.h>
#include <itkImageRegionIteratorWithIndex.h>
#include <itkImageDuplicator.h>
#include "rtkConstantImageSource.h"
#include "rtkRayEllipsoidIntersectionImageFilter.h"
#include "rtkDrawEllipsoidImageFilter.h"
#include "rtkThreeDCircularProjectionGeometry.h"
int
main(int, char *[])
{
using PixelType = float;
using DVFVectorType = itk::CovariantVector<PixelType, 3>;
using VolumeSeriesType = itk::Image<PixelType, 4>;
using ProjectionStackType = itk::Image<PixelType, 3>;
using VolumeType = itk::Image<PixelType, 3>;
using DVFSequenceImageType = itk::Image<DVFVectorType, 4>;
using GeometryType = rtk::ThreeDCircularProjectionGeometry;
constexpr unsigned int NumberOfProjectionImages = 64;
/* =========================
* Constant volume source
* ========================= */
using ConstantVolumeSourceType = rtk::ConstantImageSource<VolumeType>;
auto volumeSource = ConstantVolumeSourceType::New();
auto origin = itk::MakePoint(-63., -31., -63.);
auto spacing = itk::MakeVector(4., 4., 4.);
auto size = itk::MakeSize(32, 16, 32);
volumeSource->SetOrigin(origin);
volumeSource->SetSpacing(spacing);
volumeSource->SetSize(size);
volumeSource->Update();
/* =========================
* Projection accumulation
* ========================= */
using ConstantProjectionSourceType = rtk::ConstantImageSource<ProjectionStackType>;
auto projectionsSource = ConstantProjectionSourceType::New();
auto projOrigin = itk::MakePoint(-254., -254., -254.);
auto projSpacing = itk::MakeVector(8., 8., 1.);
auto projSize = itk::MakeSize(64, 64, NumberOfProjectionImages);
projectionsSource->SetOrigin(projOrigin);
projectionsSource->SetSpacing(projSpacing);
projectionsSource->SetSize(projSize);
projectionsSource->Update();
auto oneProjectionSource = ConstantProjectionSourceType::New();
projSize[2] = 1;
oneProjectionSource->SetOrigin(projOrigin);
oneProjectionSource->SetSpacing(projSpacing);
oneProjectionSource->SetSize(projSize);
using REIType = rtk::RayEllipsoidIntersectionImageFilter<VolumeType, ProjectionStackType>;
using PasteType = itk::PasteImageFilter<ProjectionStackType, ProjectionStackType, ProjectionStackType>;
/* Allocate explicit accumulator image */
auto accumulated = ProjectionStackType::New();
accumulated->SetOrigin(projectionsSource->GetOutput()->GetOrigin());
accumulated->SetSpacing(projectionsSource->GetOutput()->GetSpacing());
accumulated->SetDirection(projectionsSource->GetOutput()->GetDirection());
accumulated->SetRegions(projectionsSource->GetOutput()->GetLargestPossibleRegion());
accumulated->Allocate();
accumulated->FillBuffer(0.);
auto paste = PasteType::New();
paste->SetDestinationImage(accumulated);
itk::Index<3> destIndex;
destIndex.Fill(0);
// Create the signal file and the geometry, to be filled in the for loop
std::string signalFileName = "four_d_signal.txt";
std::ofstream signalFile(signalFileName.c_str());
auto geometry = GeometryType::New();
for (unsigned int i = 0; i < NumberOfProjectionImages; ++i)
{
geometry->AddProjection(600., 1200., i * 360. / NumberOfProjectionImages, 0, 0, 0, 0, 20, 15);
auto geom = GeometryType::New();
geom->AddProjection(600., 1200., i * 360. / NumberOfProjectionImages, 0, 0, 0, 0, 20, 15);
auto e1 = REIType::New();
e1->SetInput(oneProjectionSource->GetOutput());
e1->SetGeometry(geom);
e1->SetDensity(2.);
e1->SetAxis(itk::MakeVector(60., 30., 60.));
e1->SetCenter(itk::MakePoint(0., 0., 0.));
e1->InPlaceOff();
e1->Update();
auto e2 = REIType::New();
e2->SetInput(e1->GetOutput());
e2->SetGeometry(geom);
e2->SetDensity(-1.);
e2->SetAxis(itk::MakeVector(8., 8., 8.));
auto center = itk::MakePoint(4 * (std::abs((4 + i) % 8 - 4.) - 2.), 0., 0.);
e2->SetCenter(center);
e2->InPlaceOff();
e2->Update();
paste->SetSourceImage(e2->GetOutput());
paste->SetSourceRegion(e2->GetOutput()->GetLargestPossibleRegion());
paste->SetDestinationIndex(destIndex);
paste->Update();
accumulated = paste->GetOutput();
paste->SetDestinationImage(accumulated);
destIndex[2]++;
signalFile << (i % 8) / 8. << std::endl;
}
signalFile.close();
TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(accumulated, "four_d_projections.mha"));
TRY_AND_EXIT_ON_ITK_EXCEPTION(rtk::WriteGeometry(geometry, "four_d_geometry.xml"))
/* =========================
* DVF & inverse DVF
* ========================= */
auto fourDOrigin = itk::MakePoint(-63., -31., -63., 0.);
auto fourDSpacing = itk::MakeVector(4., 4., 4., 1.);
auto fourDSize = itk::MakeSize(32, 16, 32, 8);
auto dvf = DVFSequenceImageType::New();
auto idvf = DVFSequenceImageType::New();
typename DVFSequenceImageType::RegionType region;
auto dvfSize = itk::MakeSize(fourDSize[0], fourDSize[1], fourDSize[2], 2);
region.SetSize(dvfSize);
dvf->SetRegions(region);
dvf->SetOrigin(fourDOrigin);
dvf->SetSpacing(fourDSpacing);
dvf->Allocate();
idvf->SetRegions(region);
idvf->SetOrigin(fourDOrigin);
idvf->SetSpacing(fourDSpacing);
idvf->Allocate();
itk::ImageRegionIteratorWithIndex<DVFSequenceImageType> it(dvf, region);
itk::ImageRegionIteratorWithIndex<DVFSequenceImageType> iit(idvf, region);
DVFVectorType v;
typename DVFSequenceImageType::IndexType centerIndex;
centerIndex.Fill(0);
centerIndex[0] = dvfSize[0] / 2;
centerIndex[1] = dvfSize[1] / 2;
centerIndex[2] = dvfSize[2] / 2;
for (; !it.IsAtEnd(); ++it, ++iit)
{
v.Fill(0.);
auto d = it.GetIndex() - centerIndex;
if (0.3 * d[0] * d[0] + d[1] * d[1] + d[2] * d[2] < 40)
v[0] = (it.GetIndex()[3] == 0 ? -8. : 8.);
it.Set(v);
iit.Set(-v);
}
TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(dvf, "four_d_dvf.mha"));
TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(idvf, "four_d_idvf.mha"));
/* =========================
* Ground truth
* ========================= */
auto join = itk::JoinSeriesImageFilter<VolumeType, VolumeSeriesType>::New();
for (unsigned int t = 0; t < fourDSize[3]; ++t)
{
using DEType = rtk::DrawEllipsoidImageFilter<VolumeType, VolumeType>;
auto de1 = DEType::New();
de1->SetInput(volumeSource->GetOutput());
de1->SetDensity(2.);
de1->SetAxis(itk::MakeVector(60., 30., 60.));
de1->SetCenter(itk::MakePoint(0., 0., 0.));
de1->InPlaceOff();
de1->Update();
auto de2 = DEType::New();
de2->SetInput(de1->GetOutput());
de2->SetDensity(-1.);
de2->SetAxis(itk::MakeVector(8., 8., 8.));
de2->SetCenter(itk::MakePoint(4 * (std::abs((4 + t) % 8 - 4.) - 2.), 0., 0.));
de2->InPlaceOff();
de2->Update();
using DuplicatorType = itk::ImageDuplicator<VolumeType>;
auto duplicator = DuplicatorType::New();
duplicator->SetInputImage(de2->GetOutput());
duplicator->Update();
join->SetInput(t, duplicator->GetOutput());
}
join->Update();
TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(join->GetOutput(), "four_d_ground_truth.mha"));
return EXIT_SUCCESS;
}
import itk
from itk import RTK as rtk
PixelType = itk.F
DVFVectorType = itk.CovariantVector[PixelType, 3]
VolumeSeriesType = itk.Image[PixelType, 4]
ProjectionStackType = itk.Image[PixelType, 3]
VolumeType = itk.Image[PixelType, 3]
DVFSequenceImageType = itk.Image[DVFVectorType, 4]
NumberOfProjectionImages = 64
NumberOfFrames = 8
# /* =========================
# * Constant volume source
# * ========================= */
ConstantVolumeSourceType = rtk.ConstantImageSource[VolumeType]
volumeSource = ConstantVolumeSourceType.New()
origin = [-63.0, -31.0, -63.0]
spacing = [4.0, 4.0, 4.0]
size = [32, 16, 32]
volumeSource.SetOrigin(origin)
volumeSource.SetSpacing(spacing)
volumeSource.SetSize(size)
volumeSource.Update()
# /* =========================
# * Projection accumulation
# * ========================= */
ConstantProjectionSourceType = rtk.ConstantImageSource[ProjectionStackType]
projectionsSource = ConstantProjectionSourceType.New()
projOrigin = [-254.0, -254.0, -254.0]
projSpacing = [8.0, 8.0, 1.0]
projSize = [64, 64, NumberOfProjectionImages]
projectionsSource.SetOrigin(projOrigin)
projectionsSource.SetSpacing(projSpacing)
projectionsSource.SetSize(projSize)
projectionsSource.Update()
oneProjectionSource = ConstantProjectionSourceType.New()
oneProjSize = [projSize[0], projSize[1], 1]
oneProjectionSource.SetOrigin(projOrigin)
oneProjectionSource.SetSpacing(projSpacing)
oneProjectionSource.SetSize(oneProjSize)
REIType = rtk.RayEllipsoidIntersectionImageFilter[VolumeType, ProjectionStackType]
PasteType = itk.PasteImageFilter[ProjectionStackType]
# /* Allocate explicit accumulator image */
accumulated = ProjectionStackType.New()
accumulated.SetOrigin(projectionsSource.GetOutput().GetOrigin())
accumulated.SetSpacing(projectionsSource.GetOutput().GetSpacing())
accumulated.SetDirection(projectionsSource.GetOutput().GetDirection())
accumulated.SetRegions(projectionsSource.GetOutput().GetLargestPossibleRegion())
accumulated.Allocate()
accumulated.FillBuffer(0.0)
paste = PasteType.New()
paste.SetDestinationImage(accumulated)
destIndex = [0, 0, 0]
# Create the signal file and the geometry, to be filled in the for loop
signalFileName = "four_d_signal.txt"
geometry = rtk.ThreeDCircularProjectionGeometry.New()
print("Generating projections, geometry, and signal...")
with open(signalFileName, "w", encoding="utf-8") as signalFile:
for i in range(NumberOfProjectionImages):
if i % 25 == 0:
print(f" projection {i + 1}/{NumberOfProjectionImages}")
geometry.AddProjection(
600.0,
1200.0,
i * 360.0 / NumberOfProjectionImages,
0,
0,
0,
0,
20,
15,
)
geom = rtk.ThreeDCircularProjectionGeometry.New()
geom.AddProjection(
600.0,
1200.0,
i * 360.0 / NumberOfProjectionImages,
0,
0,
0,
0,
20,
15,
)
e1 = REIType.New()
e1.SetInput(oneProjectionSource.GetOutput())
e1.SetGeometry(geom)
e1.SetDensity(2.0)
e1.SetAxis([60.0, 30.0, 60.0])
e1.SetCenter([0.0, 0.0, 0.0])
e1.InPlaceOff()
e1.Update()
e2 = REIType.New()
e2.SetInput(e1.GetOutput())
e2.SetGeometry(geom)
e2.SetDensity(-1.0)
e2.SetAxis([8.0, 8.0, 8.0])
center = [4 * (abs((4 + i) % 8 - 4.0) - 2.0), 0.0, 0.0]
e2.SetCenter(center)
e2.InPlaceOff()
e2.Update()
paste.SetSourceImage(e2.GetOutput())
paste.SetSourceRegion(e2.GetOutput().GetLargestPossibleRegion())
paste.SetDestinationIndex(destIndex)
paste.Update()
accumulated = paste.GetOutput()
accumulated.DisconnectPipeline()
paste.SetDestinationImage(accumulated)
destIndex[2] += 1
signalFile.write(f"{(i % 8) / 8.0}\n")
itk.imwrite(accumulated, "four_d_projections.mha")
rtk.write_geometry(geometry, "four_d_geometry.xml")
# /* =========================
# * DVF & inverse DVF
# * ========================= */
print("Generating DVF and inverse DVF...")
fourDOrigin = [-63.0, -31.0, -63.0, 0.0]
fourDSpacing = [4.0, 4.0, 4.0, 1.0]
fourDSize = [32, 16, 32, NumberOfFrames]
dvf = DVFSequenceImageType.New()
idvf = DVFSequenceImageType.New()
region = itk.ImageRegion[4]()
dvfSize = [fourDSize[0], fourDSize[1], fourDSize[2], 2]
region.SetSize(dvfSize)
dvf.SetRegions(region)
dvf.SetOrigin(fourDOrigin)
dvf.SetSpacing(fourDSpacing)
dvf.Allocate()
idvf.SetRegions(region)
idvf.SetOrigin(fourDOrigin)
idvf.SetSpacing(fourDSpacing)
idvf.Allocate()
centerIndex = [0, 0, 0, 0]
centerIndex[0] = dvfSize[0] // 2
centerIndex[1] = dvfSize[1] // 2
centerIndex[2] = dvfSize[2] // 2
for t in range(dvfSize[3]):
for z in range(dvfSize[2]):
for y in range(dvfSize[1]):
for x in range(dvfSize[0]):
v = DVFVectorType()
v.Fill(0.0)
d0 = x - centerIndex[0]
d1 = y - centerIndex[1]
d2 = z - centerIndex[2]
if 0.3 * d0 * d0 + d1 * d1 + d2 * d2 < 40:
v[0] = -8.0 if t == 0 else 8.0
iv = DVFVectorType()
iv.Fill(0.0)
iv[0] = -v[0]
index = [x, y, z, t]
dvf.SetPixel(index, v)
idvf.SetPixel(index, iv)
itk.imwrite(dvf, "four_d_dvf.mha")
itk.imwrite(idvf, "four_d_idvf.mha")
# /* =========================
# * Ground truth
# * ========================= */
print("Generating ground truth...")
join = itk.JoinSeriesImageFilter[VolumeType, VolumeSeriesType].New()
for t in range(fourDSize[3]):
DEType = rtk.DrawEllipsoidImageFilter[VolumeType, VolumeType]
de1 = DEType.New()
de1.SetInput(volumeSource.GetOutput())
de1.SetDensity(2.0)
de1.SetAxis([60.0, 30.0, 60.0])
de1.SetCenter([0.0, 0.0, 0.0])
de1.InPlaceOff()
de1.Update()
de2 = DEType.New()
de2.SetInput(de1.GetOutput())
de2.SetDensity(-1.0)
de2.SetAxis([8.0, 8.0, 8.0])
de2.SetCenter([4 * (abs((4 + t) % 8 - 4.0) - 2.0), 0.0, 0.0])
de2.InPlaceOff()
de2.Update()
duplicator = itk.ImageDuplicator[VolumeType].New()
duplicator.SetInputImage(de2.GetOutput())
duplicator.Update()
join.SetInput(t, duplicator.GetOutput())
join.Update()
itk.imwrite(join.GetOutput(), "four_d_ground_truth.mha")
print("Done.")