SimpleITK/Advisory Review Board/Prototype Code Discussions: Difference between revisions

From KitwarePublic
Jump to navigationJump to search
No edit summary
 
(5 intermediate revisions by 3 users not shown)
Line 280: Line 280:
</pre>
</pre>


==== Enum approach ====
==== Strategy pattern ====
[http://en.wikipedia.org/wiki/Strategy_pattern http://en.wikipedia.org/wiki/Strategy_pattern]


* Notes:
* Notes:
** 2 independent issues here (enums and granularity)
** 2 independent issues here (strategy and parameter access)
** Uses enums for parameter setting so all parameter setting is done through an X.SetParameter[TYPE]("name", val) function
** 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
** Maintains registration granularity so that transform, interpolator, optimizer, and metric can be interchanged
Line 423: Line 424:


== User Domain Examples ==
== User Domain Examples ==
This section provides a set of examples for simple applications from a number of target user groups.  The goal is to present the ARB with targeted examples that show non-trivial applications specific to each target community.
=== Microscopy ===
=== Orfeo Toolbox ===
=== Education ===

Latest revision as of 17:51, 13 February 2013

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?

Streaming

  • An open question is how Filter Blocks / Procedural paradigm might support streaming. Based on Jim Miller's suggestion, we could build machinery to stream blocks through a list of filters. This assumes the readers and writers support random access to images on disk.

Simple Examples

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 ("sample/path/to/image.jpg");

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

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

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

// Write out the image
ImageFileWriter ("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();

// Anonymous reader object
Image::Pointer im = ImageFileReader().SetFilename ( "path/image.nrrd" ).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();

Strategy pattern

http://en.wikipedia.org/wiki/Strategy_pattern

  • Notes:
    • 2 independent issues here (strategy and parameter access)
    • 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");
Jim's recommendation
// Open an image
Image::Pointer im = ImageFileReader::Read("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::Filter(im, points, 128, 200); // Segment()?

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

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

User Domain Examples

This section provides a set of examples for simple applications from a number of target user groups. The goal is to present the ARB with targeted examples that show non-trivial applications specific to each target community.

Microscopy

Orfeo Toolbox

Education