ITK  5.4.0 Insight Toolkit
Examples/Iterators/NeighborhoodIterators5.cxx
/*=========================================================================
*
*
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* Unless required by applicable law or agreed to in writing, software
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
*
*=========================================================================*/
// Software Guide : BeginLatex
//
// This example introduces slice-based neighborhood processing. A slice, in
// this context, is a 1D path through an ND neighborhood. Slices are defined
// for generic arrays by the \code{std::slice} class as a start index, a step
// size, and an end index. Slices simplify the implementation of certain
// neighborhood calculations. They also provide a mechanism for taking inner
// products with subregions of neighborhoods.
//
// Suppose, for example, that we want to take partial derivatives in the $y$
// direction of a neighborhood, but offset those derivatives by one pixel
// position along the positive $x$ direction. For a $3\times3$, 2D
// neighborhood iterator, we can construct an \code{std::slice}, \code{(start
// = 2, stride = 3, end = 8)}, that represents the neighborhood offsets $(1, // -1)$, $(1, 0)$, $(1, 1)$ (see Figure~\ref{fig:NeighborhoodIteratorFig2}).
// If we pass this slice as an extra argument to the
// \doxygen{NeighborhoodInnerProduct} function, then the inner product is
// taken only along that slice. This sliced'' inner product with a 1D
// \doxygen{DerivativeOperator} gives the desired derivative.
//
// The previous separable Gaussian filtering example can be rewritten using
// slices and slice-based inner products. In general, slice-based processing
// is most useful when doing many different calculations on the same
// neighborhood, where defining multiple iterators as in
// Section~\ref{sec:NeighborhoodExample4} becomes impractical or inefficient.
// Good examples of slice-based neighborhood processing can be found in any of
// the ND anisotropic diffusion function objects, such as
// \doxygen{CurvatureNDAnisotropicDiffusionFunction}.
//
// Software Guide : EndLatex
int
main(int argc, char ** argv)
{
if (argc < 4)
{
std::cerr << "Missing parameters. " << std::endl;
std::cerr << "Usage: " << std::endl;
std::cerr << argv[0] << " inputImageFile outputImageFile sigma"
<< std::endl;
return EXIT_FAILURE;
}
using PixelType = float;
using ImageType = itk::Image<PixelType, 2>;
using NeighborhoodIteratorType = itk::ConstNeighborhoodIterator<ImageType>;
try
{
}
catch (const itk::ExceptionObject & err)
{
std::cerr << "ExceptionObject caught !" << std::endl;
std::cerr << err << std::endl;
return EXIT_FAILURE;
}
auto output = ImageType::New();
output->Allocate();
using FaceCalculatorType =
FaceCalculatorType faceCalculator;
FaceCalculatorType::FaceListType faceList;
FaceCalculatorType::FaceListType::iterator fit;
IteratorType out;
NeighborhoodIteratorType it;
// Software Guide: BeginLatex
//
// The first difference between this example and the previous example is
// that the Gaussian operator is only initialized once. Its direction is
// not important because it is only a 1D array of coefficients.
//
// Software Guide: EndLatex
// Software Guide : BeginCodeSnippet
gaussianOperator.SetDirection(0);
gaussianOperator.SetVariance(std::stod(argv[3]) * std::stod(argv[3]));
gaussianOperator.CreateDirectional();
// Software Guide : EndCodeSnippet
// Software Guide : BeginLatex
//
// Next we need to define a radius for the iterator. The radius in all
// directions matches that of the single extent of the Gaussian operator,
// defining a square neighborhood.
//
// Software Guide : EndLatex
// Software Guide : BeginCodeSnippet
// Software Guide EndCodeSnippet
// Software Guide : BeginLatex
//
// The inner product and face calculator are defined for the main processing
// loop as before, but now the iterator is reinitialized each iteration with
// inner product is taken using a slice along the axial direction
// corresponding to the current iteration. Note the use of
// \code{GetSlice()} to return the proper slice from the iterator itself.
// \code{GetSlice()} can only be used to return the slice along the complete
// extent of the axial direction of a neighborhood.
//
// Software Guide : EndLatex
// Software Guide : BeginCodeSnippet
for (unsigned int i = 0; i < ImageType::ImageDimension; ++i)
{
for (fit = faceList.begin(); fit != faceList.end(); ++fit)
{
out = IteratorType(output, *fit);
for (it.GoToBegin(), out.GoToBegin(); !it.IsAtEnd(); ++it, ++out)
{
out.Set(innerProduct(it.GetSlice(i), it, gaussianOperator));
}
}
// Swap the input and output buffers
if (i != ImageType::ImageDimension - 1)
{
ImageType::Pointer tmp = input;
input = output;
output = tmp;
}
}
// Software Guide : EndCodeSnippet
// Software Guide : BeginLatex
//
// This technique produces exactly the same results as the previous example.
// A little experimentation, however, will reveal that it is less efficient
// since the neighborhood iterator is keeping track of extra, unused pixel
// locations for each iteration, while the previous example only references
// those pixels that it needs. In cases, however, where an algorithm takes
// multiple derivatives or convolution products over the same neighborhood,
// slice-based processing can increase efficiency and simplify the
// implementation.
//
// Software Guide : EndLatex
using WritePixelType = unsigned char;
using WriteImageType = itk::Image<WritePixelType, 2>;
using RescaleFilterType =
auto rescaler = RescaleFilterType::New();
rescaler->SetOutputMinimum(0);
rescaler->SetOutputMaximum(255);
rescaler->SetInput(output);
auto writer = WriterType::New();
writer->SetFileName(argv[2]);
writer->SetInput(rescaler->GetOutput());
try
{
writer->Update();
}
catch (const itk::ExceptionObject & err)
{
std::cerr << "ExceptionObject caught !" << std::endl;
std::cerr << err << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
itk::NeighborhoodAlgorithm::ImageBoundaryFacesCalculator
Splits an image into a main region and several "face" regions which are used to handle computations o...
Definition: itkNeighborhoodAlgorithm.h:63
itk::NeighborhoodOperator::SetDirection
void SetDirection(const unsigned long direction)
Definition: itkNeighborhoodOperator.h:94
Pointer
SmartPointer< Self > Pointer
itkConstNeighborhoodIterator.h
itkNeighborhoodAlgorithm.h
itkImageRegionIterator.h
itk::NeighborhoodOperator::CreateDirectional
virtual void CreateDirectional()
Data source that reads image data from a single file.
itk::ImageRegionIterator
A multi-dimensional iterator templated over image type that walks a region of pixels.
Definition: itkImageRegionIterator.h:80
Definition: itkNeighborhood.h:129
itk::ImageFileWriter
Writes image data to a single file.
Definition: itkImageFileWriter.h:88
itk::GaussianOperator
A NeighborhoodOperator whose coefficients are a one dimensional, discrete Gaussian kernel.
Definition: itkGaussianOperator.h:69
itkRescaleIntensityImageFilter.h
itkImageFileWriter.h
itkNeighborhoodInnerProduct.h
itk::ConstNeighborhoodIterator
Const version of NeighborhoodIterator, defining iteration of a local N-dimensional neighborhood of pi...
Definition: itkConstNeighborhoodIterator.h:51
itk::RescaleIntensityImageFilter
Applies a linear transformation to the intensity levels of the input Image.
Definition: itkRescaleIntensityImageFilter.h:133
itk::Image
Templated n-dimensional image class.
Definition: itkImage.h:88
itk::NeighborhoodInnerProduct< ImageType >
New
static Pointer New()
itkGaussianOperator.h
itk::GaussianOperator::SetVariance
void SetVariance(const double variance)
Definition: itkGaussianOperator.h:82