SimpleITK/Advisory Review Board/Prototype Code Discussions: Difference between revisions
From KitwarePublic
Jump to navigationJump to search
Line 429: | Line 429: | ||
=== Orfeo Toolbox === | === Orfeo Toolbox === | ||
=== Education === |
Revision as of 14:57, 10 September 2010
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();
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");
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.