Register Image to Another Using Landmarks#
Synopsis#
Rigidly register one image to another using manually specified landmarks.
Results#
Code#
Python#
#!/usr/bin/env python3
import itk
Dimension = 2
PixelType = itk.ctype("unsigned char")
ImageType = itk.Image[PixelType, Dimension]
def create_fixed_image():
start = [
0,
] * Dimension
size = [
100,
] * Dimension
region = itk.ImageRegion[Dimension](start, size)
ImageType = itk.Image[PixelType, Dimension]
image = ImageType.New()
image.SetRegions(region)
image.Allocate()
image.FillBuffer(0)
image[11:20, 11:20] = 255
itk.imwrite(image, "fixedPython.png")
return image
def create_moving_image():
start = [
0,
] * Dimension
size = [
100,
] * Dimension
region = itk.ImageRegion[Dimension](start, size)
ImageType = itk.Image[PixelType, Dimension]
image = ImageType.New()
image.SetRegions(region)
image.Allocate()
image.FillBuffer(0)
image[51:60, 51:60] = 100
itk.imwrite(image, "movingPython.png")
return image
fixed_image = create_fixed_image()
moving_image = create_moving_image()
LandmarkPointType = itk.Point[itk.D, Dimension]
LandmarkContainerType = itk.vector[LandmarkPointType]
fixed_landmarks = LandmarkContainerType()
moving_landmarks = LandmarkContainerType()
fixed_point = LandmarkPointType()
moving_point = LandmarkPointType()
fixed_point[0] = 10
fixed_point[1] = 10
moving_point[0] = 50
moving_point[1] = 50
fixed_landmarks.push_back(fixed_point)
moving_landmarks.push_back(moving_point)
fixed_point[0] = 20
fixed_point[1] = 10
moving_point[0] = 60
moving_point[1] = 50
fixed_landmarks.push_back(fixed_point)
moving_landmarks.push_back(moving_point)
fixed_point[0] = 20
fixed_point[1] = 20
moving_point[0] = 60
moving_point[1] = 60
fixed_landmarks.push_back(fixed_point)
moving_landmarks.push_back(moving_point)
TransformInitializerType = itk.LandmarkBasedTransformInitializer[
itk.Transform[itk.D, Dimension, Dimension]
]
transform_initializer = TransformInitializerType.New()
transform_initializer.SetFixedLandmarks(fixed_landmarks)
transform_initializer.SetMovingLandmarks(moving_landmarks)
transform = itk.Rigid2DTransform[itk.D].New()
transform_initializer.SetTransform(transform)
transform_initializer.InitializeTransform()
output = itk.resample_image_filter(
moving_image,
transform=transform,
use_reference_image=True,
reference_image=fixed_image,
default_pixel_value=200,
)
itk.imwrite(output, "outputPython.png")
C++#
#include "itkImageFileWriter.h"
#include "itkImage.h"
#include "itkVector.h"
#include "itkResampleImageFilter.h"
#include "itkLandmarkBasedTransformInitializer.h"
#include "itkRigid2DTransform.h"
constexpr unsigned int Dimension = 2;
using PixelType = unsigned char;
using ImageType = itk::Image<PixelType, Dimension>;
static void
CreateFixedImage(ImageType::Pointer image);
static void
CreateMovingImage(ImageType::Pointer image);
int
main(int itkNotUsed(argc), char * itkNotUsed(argv)[])
{
auto fixedImage = ImageType::New();
CreateFixedImage(fixedImage);
auto movingImage = ImageType::New();
CreateMovingImage(movingImage);
using TransformType = itk::Rigid2DTransform<double>;
using LandmarkBasedTransformInitializerType =
itk::LandmarkBasedTransformInitializer<TransformType, ImageType, ImageType>;
LandmarkBasedTransformInitializerType::Pointer landmarkBasedTransformInitializer =
LandmarkBasedTransformInitializerType::New();
// Create source and target landmarks.
using LandmarkContainerType = LandmarkBasedTransformInitializerType::LandmarkPointContainer;
using LandmarkPointType = LandmarkBasedTransformInitializerType::LandmarkPointType;
LandmarkContainerType fixedLandmarks;
LandmarkContainerType movingLandmarks;
LandmarkPointType fixedPoint;
LandmarkPointType movingPoint;
fixedPoint[0] = 10;
fixedPoint[1] = 10;
movingPoint[0] = 50;
movingPoint[1] = 50;
fixedLandmarks.push_back(fixedPoint);
movingLandmarks.push_back(movingPoint);
fixedPoint[0] = 10;
fixedPoint[1] = 20;
movingPoint[0] = 50;
movingPoint[1] = 60;
fixedLandmarks.push_back(fixedPoint);
movingLandmarks.push_back(movingPoint);
fixedPoint[0] = 20;
fixedPoint[1] = 10;
movingPoint[0] = 60;
movingPoint[1] = 50;
fixedLandmarks.push_back(fixedPoint);
movingLandmarks.push_back(movingPoint);
fixedPoint[0] = 20;
fixedPoint[1] = 20;
movingPoint[0] = 60;
movingPoint[1] = 60;
fixedLandmarks.push_back(fixedPoint);
movingLandmarks.push_back(movingPoint);
landmarkBasedTransformInitializer->SetFixedLandmarks(fixedLandmarks);
landmarkBasedTransformInitializer->SetMovingLandmarks(movingLandmarks);
auto transform = TransformType::New();
transform->SetIdentity();
landmarkBasedTransformInitializer->SetTransform(transform);
landmarkBasedTransformInitializer->InitializeTransform();
using ResampleFilterType = itk::ResampleImageFilter<ImageType, ImageType, double>;
auto resampleFilter = ResampleFilterType::New();
resampleFilter->SetInput(movingImage);
resampleFilter->SetTransform(transform);
resampleFilter->SetUseReferenceImage(true);
resampleFilter->SetReferenceImage(fixedImage);
resampleFilter->SetDefaultPixelValue(200);
itk::WriteImage(resampleFilter->GetOutput(), "output.png");
return EXIT_SUCCESS;
}
void
CreateFixedImage(ImageType::Pointer image)
{
// Create a black image with a white square
ImageType::IndexType start;
start.Fill(0);
ImageType::SizeType size;
size.Fill(100);
ImageType::RegionType region;
region.SetSize(size);
region.SetIndex(start);
image->SetRegions(region);
image->Allocate();
image->FillBuffer(0);
itk::ImageRegionIterator<ImageType> imageIterator(image, region);
while (!imageIterator.IsAtEnd())
{
if (imageIterator.GetIndex()[0] > 10 && imageIterator.GetIndex()[0] < 20 && imageIterator.GetIndex()[1] > 10 &&
imageIterator.GetIndex()[1] < 20)
{
imageIterator.Set(255);
}
++imageIterator;
}
itk::WriteImage(image, "fixed.png");
}
void
CreateMovingImage(ImageType::Pointer image)
{
// Create a black image with a white square
ImageType::IndexType start;
start.Fill(0);
ImageType::SizeType size;
size.Fill(100);
ImageType::RegionType region;
region.SetSize(size);
region.SetIndex(start);
image->SetRegions(region);
image->Allocate();
image->FillBuffer(0);
itk::ImageRegionIterator<ImageType> imageIterator(image, region);
while (!imageIterator.IsAtEnd())
{
if (imageIterator.GetIndex()[0] > 50 && imageIterator.GetIndex()[0] < 60 && imageIterator.GetIndex()[1] > 50 &&
imageIterator.GetIndex()[1] < 60)
{
imageIterator.Set(100);
}
++imageIterator;
}
itk::WriteImage(image, "moving.png");
}
Classes demonstrated#
-
template<typename TTransform, typename TFixedImage = itk::ImageBase<TTransform::InputSpaceDimension>, typename TMovingImage = itk::ImageBase<TTransform::OutputSpaceDimension>>
class LandmarkBasedTransformInitializer : public itk::Object This class computes the transform that aligns the fixed and moving images given a set of pair landmarks. The class is templated over the Transform type as well as fixed image and moving image types. The transform computed gives the best fit transform that maps the fixed and moving images in a least squares sense. The indices are taken to correspond, so point 1 in the first set will get mapped close to point 1 in the second set, etc.
Currently, the following transforms are supported by the class: VersorRigid3DTransform Rigid2DTransform AffineTransform BSplineTransform
An equal number of fixed and moving landmarks need to be specified using SetFixedLandmarks() and SetMovingLandmarks(). Any number of landmarks may be specified. In the case of the Affine transformation the number of landmarks must be greater than the landmark dimensionality. If this is not the case an exception is thrown. In the case of the VersorRigid3DTransform and Rigid2DTransform the number of landmarks must be equal or greater than the landmark dimensionality. If this is not the case, only the translational component of the transformation is computed and the rotation is the identity. In the case of using Affine or BSpline transforms, each landmark pair can contribute in the final transform based on its defined weight. Number of weights should be equal to the number of landmarks and can be specified using SetLandmarkWeight(). By defaults are weights are set to one. Call InitializeTransform() to initialize the transform.
The class is based in part on Hybrid/vtkLandmarkTransform originally implemented in python by David G. Gobbi.
The solution is based on Berthold K. P. Horn (1987), “Closed-form solution of absolute orientation
using unit quaternions,”
http://people.csail.mit.edu/bkph/papers/Absolute_Orientation.pdfThe Affine Transform
initializer is based on an algorithm by H Spaeth, and is described in the Insight Journal Article “Affine Transformation for Landmark Based Registration Initializer
in ITK” by Kim E.Y., Johnson H., Williams N. available at
http://midasjournal.com/browse/publication/825- ITK Sphinx Examples: