CompositeFilterExample.cxxΒΆ
Example source code (CompositeFilterExample.cxx):
// The composite filter we will build combines three filters: a gradient
// magnitude operator, which will calculate the first-order derivative of
// the image; a thresholding step to select edges over a given strength;
// and finally a rescaling filter, to ensure the resulting image data is
// visible by scaling the intensity to the full spectrum of the output
// image type.
//
// Since this filter takes an image and produces another image (of
// identical type), we will specialize the ImageToImageFilter:
// Next we include headers for the component filters:
#include "itkUnaryFunctorImageFilter.h"
#include "itkGradientMagnitudeImageFilter.h"
#include "itkThresholdImageFilter.h"
#include "itkRescaleIntensityImageFilter.h"
#include "itkNumericTraits.h"
#include "otbImage.h"
// Now we can declare the filter itself. It is within the OTB namespace,
// and we decide to make it use the same image type for both input and
// output, thus the template declaration needs only one parameter.
// Deriving from \code{ImageToImageFilter} provides default behavior for
// several important aspects, notably allocating the output image (and
// making it the same dimensions as the input).
namespace otb
{
template <class TImageType>
class ITK_EXPORT CompositeExampleImageFilter : public itk::ImageToImageFilter<TImageType, TImageType>
{
public:
// Next we have the standard declarations, used for object creation with
// the object factory:
using Self = CompositeExampleImageFilter<TImageType>;
using Superclass = itk::ImageToImageFilter<TImageType, TImageType>;
using Pointer = itk::SmartPointer<Self>;
using ConstPointer = itk::SmartPointer<const Self>;
/** Method for creation through object factory */
itkNewMacro(Self);
/** Run-time type information */
itkTypeMacro(CompositeExampleImageFilter, itk::ImageToImageFilter);
/** Display */
void PrintSelf(std::ostream& os, itk::Indent indent) const override;
// Here we declare an alias (to save typing) for the image's pixel type,
// which determines the type of the threshold value. We then use the
// convenience macros to define the Get and Set methods for this parameter.
using PixelType = typename TImageType::PixelType;
itkGetMacro(Threshold, PixelType);
itkSetMacro(Threshold, PixelType);
protected:
CompositeExampleImageFilter();
// Now we can declare the component filter types, templated over the
// enclosing image type:
protected:
using ThresholdType = itk::ThresholdImageFilter<TImageType>;
using GradientType = itk::GradientMagnitudeImageFilter<TImageType, TImageType>;
using RescalerType = itk::RescaleIntensityImageFilter<TImageType, TImageType>;
void GenerateData() override;
private:
CompositeExampleImageFilter(Self&); // intentionally not implemented
void operator=(const Self&); // intentionally not implemented
// The component filters are declared as data members, all using the smart
// pointer types.
typename GradientType::Pointer m_GradientFilter;
typename ThresholdType::Pointer m_ThresholdFilter;
typename RescalerType::Pointer m_RescaleFilter;
PixelType m_Threshold;
};
} /* namespace otb */
// The constructor sets up the pipeline, which involves creating the
// stages, connecting them together, and setting default parameters.
namespace otb
{
template <class TImageType>
CompositeExampleImageFilter<TImageType>::CompositeExampleImageFilter()
{
m_GradientFilter = GradientType::New();
m_ThresholdFilter = ThresholdType::New();
m_RescaleFilter = RescalerType::New();
m_ThresholdFilter->SetInput(m_GradientFilter->GetOutput());
m_RescaleFilter->SetInput(m_ThresholdFilter->GetOutput());
m_Threshold = 1;
m_RescaleFilter->SetOutputMinimum(itk::NumericTraits<PixelType>::NonpositiveMin());
m_RescaleFilter->SetOutputMaximum(itk::NumericTraits<PixelType>::max());
}
// The \code{GenerateData()} is where the composite magic happens. First,
// we connect the first component filter to the inputs of the composite
// filter (the actual input, supplied by the upstream stage). Then we
// graft the output of the last stage onto the output of the composite,
// which ensures the filter regions are updated. We force the composite
// pipeline to be processed by calling \code{Update()} on the final stage,
// then graft the output back onto the output of the enclosing filter, so
// it has the result available to the downstream filter.
template <class TImageType>
void CompositeExampleImageFilter<TImageType>::GenerateData()
{
m_GradientFilter->SetInput(this->GetInput());
m_ThresholdFilter->ThresholdBelow(this->m_Threshold);
m_RescaleFilter->GraftOutput(this->GetOutput());
m_RescaleFilter->Update();
this->GraftOutput(m_RescaleFilter->GetOutput());
}
// Finally we define the \code{PrintSelf} method, which (by convention)
// prints the filter parameters. Note how it invokes the superclass to
// print itself first, and also how the indentation prefixes each line.
//
template <class TImageType>
void CompositeExampleImageFilter<TImageType>::PrintSelf(std::ostream& os, itk::Indent indent) const
{
Superclass::PrintSelf(os, indent);
os << indent << "Threshold:" << this->m_Threshold << std::endl;
}
} /* end namespace otb */
// It is important to note that in the above example, none of the internal
// details of the pipeline were exposed to users of the class. The interface
// consisted of the Threshold parameter (which happened to change the value in
// the component filter) and the regular ImageToImageFilter interface. This
// example pipeline is illustrated in
// Figure~\ref{fig:CompositeExamplePipeline}.
#include "otbImageFileReader.h"
#include "otbImageFileWriter.h"
int main(int argc, char* argv[])
{
if (argc < 3)
{
std::cerr << "Usage: " << std::endl;
std::cerr << argv[0] << " inputImageFile outputImageFile" << std::endl;
return EXIT_FAILURE;
}
using ImageType = otb::Image<short, 2>;
using ReaderType = otb::ImageFileReader<ImageType>;
using WriterType = otb::ImageFileWriter<ImageType>;
using FilterType = otb::CompositeExampleImageFilter<ImageType>;
ReaderType::Pointer reader = ReaderType::New();
WriterType::Pointer writer = WriterType::New();
FilterType::Pointer filter = FilterType::New();
reader->SetFileName(argv[1]);
filter->SetInput(reader->GetOutput());
filter->SetThreshold(20);
writer->SetInput(filter->GetOutput());
writer->SetFileName(argv[2]);
try
{
writer->Update();
}
catch (itk::ExceptionObject& e)
{
std::cerr << "Error: " << e << std::endl;
}
return EXIT_SUCCESS;
}