[Insight-users] pointset to image registration tuning
Dean Inglis
dean.inglis at camris.ca
Sat Oct 1 15:46:24 EDT 2011
I am registering a set of (19) points obtained from a touch probe on
a subject's face. I have a 3D CT scan of the subject's head that I have
processed into a cost image by the following sequence:
1) ImageFileReader - read in an unsigned short 3D CT image (background has
value 0)
2) ShrinkImageFilter - shrink all dimensions by a factor of 2
3) ImageRegionConstIteratorWithIndex - find the first pixel index that has a
value of 0
4) ConfidenceConnectedImageFilter - threshold to unsigned char using the
seed from 3)
5) SliceBySliceImageFilter / BinaryFillholeImageFilter - fill all holes
slice by slice with a value of 0
- now there is an image of the background (255) with the entire region
within the surface of the
subject set to 0, sinuses and ear canals etc removed
6) GradientMagnitudeRecursiveGaussianImageFilter - generates an image of
the subject's surface
7) MinimumMaximumImageCalculator - find the maximum gradient magnitude
corresponding to strong edges
8) IntensityWindowingImageFilter - rescale the gradient magnitude image to
0 - 255
9) CastImageFilter - cast to unsigned char
I then have a pointset to image registration pipeline defined in the code
below.
I have tried setting the transform center to the fixed points center
and an initial translation to the difference between the fixed points and
center of gravity
of the image. I have passed in the points both in the original perturbed
positions (non-registered)
and in a manually registered (using paraview transformation filter)
configuration.
The pipeline should give a near zero output in the latter case, but does
not! I seem
to have hit a wall on tuning the optimizer and transform, as no matter what
I have tried,
(vary step lengths, number of iterations etc) I never get a satisfactory
(not even close!) registration.
Any insight would be greatly appreciated.
Dean
///////////////////////////////////
const unsigned int Dimension = 3;
typedef unsigned char FixedPointDataType;
typedef itk::PointSet< FixedPointDataType, Dimension > FixedPointSetType;
FixedPointSetType::Pointer fixedPointSet = FixedPointSetType::New();
typedef FixedPointSetType::PointsContainer FixedPointsContainer;
FixedPointsContainer::Pointer fixedPointContainer =
FixedPointsContainer::New();
typedef FixedPointSetType::PointType FixedPointType;
FixedPointType fixedPoint;
// Read the file containing coordinates of fixed points.
std::ifstream fixedFile;
fixedFile.open( argv[1] );
if( fixedFile.fail() )
{
std::cerr << "Error opening points file with name : " << std::endl;
std::cerr << argv[1] << std::endl;
return EXIT_FAILURE;
}
unsigned int pointId = 0;
double cx = 0.;
double cy = 0.;
double cz = 0.;
while( !fixedFile.eof() )
{
fixedFile >> fixedPoint;
fixedPointContainer->InsertElement( pointId, fixedPoint );
std::cout << "point " << pointId << ": " << fixedPoint[0] << ", " <<
fixedPoint[1] << ", " << fixedPoint[2] << std::endl;
cx += fixedPoint[0];
cy += fixedPoint[1];
cz += fixedPoint[2];
pointId++;
}
fixedPointSet->SetPoints( fixedPointContainer );
int numFixedPts = fixedPointSet->GetNumberOfPoints();
cx /= numFixedPts;
cy /= numFixedPts;
cz /= numFixedPts;
std::cout << "number of fixed Points = " << numFixedPts << std::endl;
std::string inputFileName = argv[2];
// Setup image types
typedef itk::Image< float, 3 > FloatImageType;
typedef itk::Image< unsigned char, 3 > UnsignedCharImageType;
// image reader
// read in the cost image
//
typedef itk::ImageFileReader< UnsignedCharImageType > ImageReaderType;
ImageReaderType::Pointer reader = ImageReaderType::New();
reader->SetFileName( inputFileName );
reader->ReleaseDataFlagOff();
reader->Update();
std::cout << "image read in " << std::endl;
FixedPointDataType defaultValue = 128;
for(int i = 0 ; i < numFixedPts; ++i )
{
fixedPointSet->SetPointData( i, defaultValue );
}
typedef itk::Vector<double,3> VectorType;
typedef itk::ImageMomentsCalculator<UnsignedCharImageType>
MomentCalculatorType;
/* Compute the moments */
MomentCalculatorType::Pointer moments = MomentCalculatorType::New();
moments->SetImage( reader->GetOutput() );
moments->Compute();
moments->Print( std::cout );
VectorType ccg = moments->GetCenterOfGravity();
std::cout << "image cofg: " << ccg[0] << ", " << ccg[1] << ", " <<
ccg[2] << std::endl;
//-----------------------------------------------------------
// Set up a Transform - does rotation (using versors), adn translation,
scaling not required
//-----------------------------------------------------------
typedef double CoordinateRepresentationType;
typedef itk::VersorRigid3DTransform< CoordinateRepresentationType >
TransformType;
TransformType::Pointer transform = TransformType::New();
transform->SetIdentity();
TransformType::InputPointType centerFixed;
centerFixed[0] = cx;
centerFixed[1] = cy;
centerFixed[2] = cz;
transform->SetCenter(centerFixed);
TransformType::OutputVectorType translation;
translation[0] = ccg[0] - cx;
translation[1] = ccg[1] - cy;
translation[2] = ccg[2] - cz;
transform->SetTranslation( translation );
//-----------------------------------------------------------
// Set up Metric (also known as a cost function)
// must inherit from itkPointSetToImageMetric
// other options are:
// itkMeanReciprocalSquareDifferencePointSetToImageMetric
// itkMeanSquaresPointSetToImageMetric
// itkNormalizedCorrelationPointSetToImageMetric
//-----------------------------------------------------------
typedef itk::MeanReciprocalSquareDifferencePointSetToImageMetric<
FixedPointSetType, UnsignedCharImageType > MetricType;
MetricType::Pointer metric = MetricType::New();
metric->SetLambda( 10 );
typedef MetricType::TransformType TransformBaseType;
typedef TransformBaseType::ParametersType ParametersType;
//------------------------------------------------------------
// Set up transform parameters - not used
//------------------------------------------------------------
/*
ParametersType parameters( transform->GetNumberOfParameters() );
// Versor type
typedef TransformType::VersorType VersorType;
VersorType versor;
parameters[0] = versor.GetX();
parameters[1] = versor.GetY();
parameters[2] = versor.GetZ();
parameters[3] = 0.0; // Translation
parameters[4] = 0.0;
parameters[5] = 0.0;
parameters[6] = 1.0; // Scale
transform->SetParameters( parameters );
*/
//------------------------------------------------------------
// Set up an Interpolator
//------------------------------------------------------------
typedef itk:: NearestNeighborInterpolateImageFunction<
UnsignedCharImageType,
CoordinateRepresentationType > InterpolatorType;
InterpolatorType::Pointer interpolator = InterpolatorType::New();
//------------------------------------------------------------
// Optimizer Type
//------------------------------------------------------------
typedef itk::VersorRigid3DTransformOptimizer OptimizerType;
OptimizerType::Pointer optimizer = OptimizerType::New();
unsigned long numberOfIterations = 500;
double maximumStepLength = 1.0; // no step will be larger than
this
double minimumStepLength = 0.001; // convergence criterion
double gradientTolerance = 1e-6; // convergence criterion
// Scale the translation components of the Transform in the Optimizer
OptimizerType::ScalesType scales( transform->GetNumberOfParameters() );
scales.Fill( 1.0 );
// set up the translation scales: translations are much larger than
rotations
scales[3] = 1./100.;
scales[4] = 1./100.;
scales[5] = 1./100.;
optimizer->SetScales( scales );
optimizer->SetNumberOfIterations( numberOfIterations );
optimizer->SetMinimumStepLength( minimumStepLength );
optimizer->SetMaximumStepLength( maximumStepLength );
optimizer->SetGradientMagnitudeTolerance( gradientTolerance );
optimizer->MinimizeOff();
optimizer->MaximizeOn();
typedef IterationCallback< OptimizerType > IterationCallbackType;
IterationCallbackType::Pointer callback = IterationCallbackType::New();
callback->SetOptimizer( optimizer );
//------------------------------------------------------------
// registration method
//------------------------------------------------------------
typedef itk::PointSetToImageRegistrationMethod< FixedPointSetType,
UnsignedCharImageType > RegistrationType;
RegistrationType::Pointer registration = RegistrationType::New();
//------------------------------------------------------
// Connect all the components required for Registration
//------------------------------------------------------
registration->SetMetric( metric );
registration->SetOptimizer( optimizer );
registration->SetTransform( transform );
registration->SetFixedPointSet( fixedPointSet );
registration->SetMovingImage( reader->GetOutput() );
registration->SetInterpolator( interpolator );
registration->SetInitialTransformParameters( transform->GetParameters());
std::cout << "starting registration .. " << std::endl;
try
{
registration->StartRegistration();
More information about the Insight-users
mailing list