Visualize a Static Sparse Shi 2D Level-Set Layers#

Synopsis#

Convert a binary mask into a sparse Shi level-set function and write its layers as an image, by sampling the function at every pixel. The mask comes from an Otsu threshold of the input image.

The written values are -3, -1, 1, and 3.

Results#

Input image (cells)

Input image#

Code#

C++#

#include "itkBinaryImageToLevelSetImageAdaptor.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkImageRegionIteratorWithIndex.h"
#include "itkShiSparseLevelSetImage.h"
#include "itkOtsuMultipleThresholdsImageFilter.h"
#include "itkRescaleIntensityImageFilter.h"

int
main(int argc, char * argv[])
{
  if (argc != 3)
  {
    std::cerr << "Missing Arguments" << std::endl;
    std::cerr << argv[0] << std::endl;
    std::cerr << "<Input Image> <Output Image>" << std::endl;
    return EXIT_FAILURE;
  }

  constexpr unsigned int Dimension = 2;

  using InputPixelType = unsigned char;
  using InputImageType = itk::Image<InputPixelType, Dimension>;

  InputImageType::Pointer input = itk::ReadImage<InputImageType>(argv[1]);

  using LevelSetType = itk::ShiSparseLevelSetImage<Dimension>;

  // Generate a binary mask that will be used as initialization for the level
  // set.
  using OtsuFilterType = itk::OtsuMultipleThresholdsImageFilter<InputImageType, InputImageType>;
  auto otsu = OtsuFilterType::New();
  otsu->SetInput(input);
  otsu->SetNumberOfHistogramBins(256);
  otsu->SetNumberOfThresholds(1);

  using RescaleType = itk::RescaleIntensityImageFilter<InputImageType, InputImageType>;
  auto rescaler = RescaleType::New();
  rescaler->SetInput(otsu->GetOutput());
  rescaler->SetOutputMinimum(0);
  rescaler->SetOutputMaximum(1);
  rescaler->Update();

  // Convert the binary mask to a sparse level-set function
  using BinaryImageToLevelSetType = itk::BinaryImageToLevelSetImageAdaptor<InputImageType, LevelSetType>;
  auto adaptor = BinaryImageToLevelSetType::New();
  adaptor->SetInputImage(rescaler->GetOutput());
  adaptor->Initialize();

  LevelSetType::Pointer levelSet = adaptor->GetModifiableLevelSet();

  // Sample the level-set function on the image grid to show its layers
  using LayerImageType = itk::Image<LevelSetType::OutputType, Dimension>;
  auto layers = LayerImageType::New();
  layers->CopyInformation(input);
  layers->SetRegions(input->GetLargestPossibleRegion());
  layers->Allocate();

  itk::ImageRegionIteratorWithIndex<LayerImageType> it(layers, layers->GetLargestPossibleRegion());
  for (it.GoToBegin(); !it.IsAtEnd(); ++it)
  {
    it.Set(levelSet->Evaluate(it.GetIndex()));
  }

  try
  {
    itk::WriteImage(layers, argv[2]);
  }
  catch (const itk::ExceptionObject & error)
  {
    std::cerr << "Error: " << error << std::endl;
    return EXIT_FAILURE;
  }

  return EXIT_SUCCESS;
}

Classes demonstrated#

template<unsigned int VDimension>
class ShiSparseLevelSetImage : public itk::LevelSetSparseImage<int8_t, VDimension>

Derived class for the shi representation of level-set function.

This representation is a “sparse” level-set function, where values could only be { -3, -1, +1, +3 } and organized into 2 layers { -1, +1 }.

Template Parameters:

VDimension – Dimension of the input space

See itk::ShiSparseLevelSetImage for additional documentation.