SimpleITK/Advisory Review Board/Prototype Code Discussions

From KitwarePublic
Jump to navigationJump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

General Design Questions

  • Procedural -vs- Pipelined:
    • The procedural model sees each filter as an independent function that takes a set of inputs and produces an output.
      • Could either be purely functional (static functions, no need to instantiate a filter) or set up with blocks of code for each filter
      • Generally easier to learn than the pipelined model (similar to Matlab's style)
    • The pipelined model sees each filter as a step along an algorithm pipeline that can take the output of a previous filter, modify it, and pass it off to the next filter in line.
      • Largely a mirror of the current ITK implementation with the templates hidden
      • Can be used to stream large images
  • Enums -vs- Parameter methods:
    • This refers to the convention for setting filter parameters
    • enums => filter.SetParameter("name", val);
      • Single method or a set of type specific methods used throughout toolkit
    • parameter methods => filter.SetName(val);
      • This is how it's currently done in ITK
  • Registration granularity:
    • Should transform, metric, optimizer, interpolation be modular like in ITK?

Gaussian Blur

  • Open an image
  • Filter the image with a Gaussian blur using sigma = 2
  • Write the image back out

Procedural

  • Pros:
    • Fewest lines of code to get a job done
    • Easy to learn/use ("Matlab-like")
  • Cons:
    • Biggest diversion from traditional ITK style
    • Potentially long list of parameters for a given function
// Read the image
Image::Pointer im = ImageFileReader::Execute("sample/path/to/image.jpg");

// Apply Gaussian with sigma = 2
im = GaussianFilter::Execute(im, 2);

// Write out the image
ImageFileWriter::Execute(im, "sample/path/to/output.png");
Jim's recommendation
// Read the image
Image::Pointer im = ImageFileReader::Read("sample/path/to/image.jpg");

// Apply Gaussian with sigma = 2
im = GaussianFilter::Filter(im, 2);

// Write out the image
ImageFileWriter::Write("sample/path/to/output.png", im);

Filter Blocks

  • Pros:
    • Each filter processes an image directly
    • Default parameters make provide good guess for algorithm prototyping
    • Parameters can be changed individually as needed
  • Cons:
    • Image is fully processed between each filter (No easy streaming implementation)
  • Other Notes:
    • No pipeline
    • Uses object-oriented paradigm so filters must be instantiated


// Read the image
ImageFileReader reader;
reader.SetFilename( "sample/path/to/image.jpg" );
Image::Pointer im = reader.execute();

// Apply Gaussian with sigma = 2
Gaussian filter;
filter.SetSigma( 2 );
im = filter.execute( im );

// Write out the image
ImageFileWriter writer;
writer.SetFilename( "sample/path/to/output.png" );
writer.execute( im );
Jim's recommendation
// Read the image
ImageFileReader reader;
reader.SetFilename( "sample/path/to/image.jpg" );
Image::Pointer im = reader.Read();

// Apply Gaussian with sigma = 2
Gaussian filter;
filter.SetSigma( 2 );
im = filter.Filter( im );

// Write out the image
ImageFileWriter writer;
writer.SetFilename( "sample/path/to/output.png" );
writer.Write( im );

Pipelined

  • Pros:
    • Can easily implement streaming for large images
    • Default parameter list is good for prototyping
    • Can set individual parameters as needed
  • Cons:
    • Pipeline paradigm may be steeper on-ramp for new users
  • Other Notes:
    • Closest to full ITK implementation with templates removed
// Read the image
ImageFileReader reader;
reader.SetFilename( "sample/path/to/image.jpg" );

// Apply Gaussian with sigma = 2
Gaussian filter;
filter.SetSigma( 2 );
filter.SetInput( reader.getOutput() );

// Write out the image
ImageFileWriter writer;
writer.SetFilename( "sample/path/to/output.png" );
writer.SetInput( filter->GetOutput() );

// Execute the pipieline
writer.Update();

Image Registration

  • Open two images (one fixed, one moving)
  • Register the moving image to the fixed image using affine registration
  • Resample the moving image using the computed transform
  • Write the resampled image out

Procedural

  • Pros:
    • Fewest lines of code
  • Cons:
    • Long argument lists for functions
    • Not easily extensible w.r.t. transform type, optimization type, interpolation type, etc...
  • Other Notees:
    • Most "Matlab-like"
// Open the fixed and moving images
Image::Pointer fixedImage = ImageFileReader::Execute( "path/to/fixed.jpg" );
Image::Pointer movingImage = ImageFileReader::Execute( "path/to/moving.jpg" );

// Register the moving image to the fixed image
AffineTransform::Pointer transform AffineRegistrator::Execute( fixedImage, movingImage );

// Resample the moving image
movingImage = ImageResampler::Execute( movingImage, transform );

// Write out the resampled image
ImageFileWriter::Ececute( movingImage, "path/to/output.png" );
Jim's recommendation
// Open the fixed and moving images
Image::Pointer fixedImage = ImageFileReader::Read( "path/to/fixed.jpg" );
Image::Pointer movingImage = ImageFileReader::Read( "path/to/moving.jpg" );

// Register the moving image to the fixed image
AffineTransform::Pointer transform AffineRegistrator::Register( fixedImage, movingImage );

// Resample the moving image
movingImage = ImageResampler::Resample( movingImage, transform );

// Write out the resampled image
ImageFileWriter::Write( "path/to/output.png", movingImage );

Filter blocks

  • Pros:
    • Default parameters make provide good guess for algorithm prototyping
    • Parameters can be changed individually as needed
  • Cons:
    • Image is fully processed when execute is called (No easy streaming implementation)
  • Other Notes:
    • No pipeline
    • Uses object-oriented paradigm
// Open the fixed and moving images
ImageFileReader reader;
reader.SetFilename( "path/to/fixed.jpg" );
Image::Pointer fixedImage = reader.execute();
reader.SetFilename( "path/to/moving.jpg" );
Image::Pointer movingImage = reader.execute();

// Register the moving image to the fixed image
AffineRegistrator registrator;
registrator.SetFixedImage( fixedImage );
registrator.SetMovingImage( movingImage );
AffineTransform transform;
transform = registrator.execute();

// Resample the moving image
Resampler resampler;
resampler.SetTransform( transform );
movingImage = resampler.execute( movingImage );

// Write out the resampled image
ImageFileWriter writer;
writer.SetFilename( "path/to/output.png" );
writer.ececute( movingImage );
Jim's recommendation
// Open the fixed and moving images
ImageFileReader reader;
reader.SetFilename( "path/to/fixed.jpg" );
Image::Pointer fixedImage = reader.Read();
reader.SetFilename( "path/to/moving.jpg" );
Image::Pointer movingImage = reader.Read();

// Register the moving image to the fixed image
AffineRegistrator registrator;
registrator.SetFixedImage( fixedImage );
registrator.SetMovingImage( movingImage );
AffineTransform transform;
transform = registrator.Register();

// Resample the moving image
Resampler resampler;
resampler.SetTransform( transform );
movingImage = resampler.Resample( movingImage );

// Write out the resampled image
ImageFileWriter writer;
writer.SetFilename( "path/to/output.png" );
writer.Write( movingImage );

Pipeline

// Open the fixed and moving images
ImageFileReader reader1;
ImageFileReader reader2;
reader1.SetFilename( "path/to/fixed.jpg" );
reader2.SetFilename( "path/to/moving.jpg" );

// Register the moving image to the fixed image
AffineRegistrator registrator;
registrator.SetFixedImage( reader1.GetOutput() );
registrator.SetMovingImage( reader2.GetOutput() );

// Resample the moving image
Resampler resampler;
resampler.SetInput( reader2.GetOutput() );
resampler.SetTransform( registrator.GetOutput() );

// Write out the resampled image
ImageFileWriter writer;
writer.SetFilename( "path/to/output.png" );
writer.SetInput( movingImage );

// Execute the pipeline
writer.Update();

Enum approach

  • Notes:
    • 2 independent issues here (enums and granularity)
    • Uses enums for parameter setting so all parameter setting is done through an X.SetParameter[TYPE]("name", val) function
    • Maintains registration granularity so that transform, interpolator, optimizer, and metric can be interchanged
// NOTE: Language = C#
// Create metric
itk::simple::simpleMetric metric;
metric.setType( itk::simple::MattesMutualInformation );
metric.setParameterInt( "NumberOfHistogramBins", 30 );
metric.setParameterInt( "NumberOfSpatialSamples", 1000 );

// Create interpolator
itk::simple::simpleInterpolator interpolator;
interpolator.setType( itk::simple::LanczosWindowedSincInterpolation );

// Create transform
itk::simple::simpleTransform transform;
transform.setType( itk::simple::AffineTransform );

// Create optimizer
itk::simple::simpleOptimizer optimizer;
optimizer.setType( itk::simple::RegularStepGradientDescentOptimizer );
optimizer.setParameterInt( "NumberOfIterations", 100 );
optimizer.setParameterDouble( "MinimumStepLength", 0.005 );
optimizer.setParameterDouble( "MaximumStepLength", 1.0 );
optimizer.setParameterBoolean( "Maximize", true );

// Registration
itk::simple::simpleRegistration registration;
registration.setMetric( metric );
registration.setInterpolator( interpolator );
registration.setTransform( transform );
registration.setOptimizer( optimizer );
registration.setFixedImage( fixedImage );
registration.setMovingImage( movingImage );

Level Set

Region Growing

  • Open an image
  • Set up a set of seed points
  • Run an connected threshold region growing segmentation
  • Write out the resulting segmentation mask

Procedural

  • Pros:
    • Fewest lines of code
  • Cons:
    • Not modular
    • More complex argument lists needed for different comparison types
// Open an image
Image::Pointer im = ImageFileReader::Execute("sample/path/to/image.jpg");

// Set up seeds
std::vector<Point> points;
Point p1, p2;
p1[0] = 25;
p1[1] = 25;
p2[0] = 35;
p2[1] = 5;
points.push_back(p1);
points.push_back(p2);

// Run the region growing algorithm
seg = RegionGrowingConnectedThresholdFilter::Execute(im, points, 128, 200);

// Write out the resulting segmentation
ImageFileWriter::Execute(seg, "sample/path/to/output.png");


Pipieline

  • Pros:
    • Can implement streaming for large images
    • More efficient
  • Cons:
    • Harder to learn that procedural
// Open an image
ImageFileReader reader;
reader.SetFilename( "sample/path/to/image.jpg" );

// Set up seeds
std::vector<Point> points;
Point p1, p2;
p1[0] = 25;
p1[1] = 25;
p2[0] = 35;
p2[1] = 5;
points.push_back(p1);
points.push_back(p2);

// Set up the region growing algorithm
RegionGrowingConnectedThresholdFilter filter;
filter->SetInput(reader->GetOutput());
filter->SetSeedPoints(points);
filter->SetLower(128);
filter->SetUpper(200);

// Write out the image
ImageFileWriter writer;
writer.SetFilename( "sample/path/to/output.png" );
writer.SetInput( filter->GetOutput() );

// Update the pipieline
writer.Update();

Watershed

QuadEdgeMesh