From afa84dc97029ada678d3fdc4e63ffa2b94e8fa79 Mon Sep 17 00:00:00 2001 From: Jonathan Guinet <jonathan.guinet@c-s.fr> Date: Fri, 13 May 2011 17:29:20 +0200 Subject: [PATCH] ENH: make the ConnectedComponent/LabelMap filters compatible with the pipeline (do not request LargestPossibleRegion) --- .../otbLabelObjectOpeningMuParserFilter.h | 6 +- .../otbLabelObjectOpeningMuParserFilter.txx | 45 ++- .../otbRelabelComponentImageFilter.h | 306 ++++++++++++++++++ .../otbRelabelComponentImageFilter.txx | 259 +++++++++++++++ ...bBandsStatisticsAttributesLabelMapFilter.h | 6 + ...andsStatisticsAttributesLabelMapFilter.txx | 33 ++ ...abelImageToLabelMapWithAdjacencyFilter.txx | 32 +- Code/OBIA/otbLabelMapToLabelImageFilter.h | 95 ++++++ Code/OBIA/otbLabelMapToLabelImageFilter.txx | 69 ++++ Code/OBIA/otbShapeAttributesLabelMapFilter.h | 26 +- .../OBIA/otbShapeAttributesLabelMapFilter.txx | 94 +++++- 11 files changed, 941 insertions(+), 30 deletions(-) create mode 100644 Code/BasicFilters/otbRelabelComponentImageFilter.h create mode 100644 Code/BasicFilters/otbRelabelComponentImageFilter.txx create mode 100644 Code/OBIA/otbLabelMapToLabelImageFilter.h create mode 100644 Code/OBIA/otbLabelMapToLabelImageFilter.txx diff --git a/Code/BasicFilters/otbLabelObjectOpeningMuParserFilter.h b/Code/BasicFilters/otbLabelObjectOpeningMuParserFilter.h index 2f574df3f1..b34c95a2f4 100644 --- a/Code/BasicFilters/otbLabelObjectOpeningMuParserFilter.h +++ b/Code/BasicFilters/otbLabelObjectOpeningMuParserFilter.h @@ -33,7 +33,6 @@ #include "otbParser.h" #include "otbMacro.h" #include "itkMacro.h" -#include "otbMaskMuParserFunctor.h" #include "itkInPlaceLabelMapFilter.h" #include "itkLabelObjectAccessors.h" @@ -80,6 +79,7 @@ typedef typename ImageType::Pointer ImagePointer; typedef typename ImageType::ConstPointer ImageConstPointer; typedef typename ImageType::PixelType PixelType; typedef typename ImageType::IndexType IndexType; +typedef typename ImageType::RegionType InputImageRegionType; typedef typename ImageType::LabelObjectType LabelObjectType; typedef typename LabelObjectType::ConstPointer LabelObjectConstPointer; typedef TFunction FunctorType; @@ -113,6 +113,10 @@ void DisplayVar() const; /** return list of Mu Parser varialbes and address**/ const std::map<std::string, double*>& GetVar() const; +void GenerateInputRequestedRegion(); + +void EnlargeOutputRequestedRegion(itk::DataObject *){}; + void GenerateData(); protected : diff --git a/Code/BasicFilters/otbLabelObjectOpeningMuParserFilter.txx b/Code/BasicFilters/otbLabelObjectOpeningMuParserFilter.txx index 2ce413ce14..364d98f2fb 100644 --- a/Code/BasicFilters/otbLabelObjectOpeningMuParserFilter.txx +++ b/Code/BasicFilters/otbLabelObjectOpeningMuParserFilter.txx @@ -100,16 +100,7 @@ void LabelObjectOpeningMuParserFilter<TImage, TFunction>::DisplayVar() const template< class TImage, class TFunction> bool LabelObjectOpeningMuParserFilter<TImage, TFunction>::CheckExpression() { - try - { - this->m_Functor.CheckExpression(); - } - catch(itk::ExceptionObject& err) - { - itkWarningMacro(<< err); - return false; - } - return true; + return this->m_Functor.CheckExpression(); } template< class TImage, class TFunction> @@ -118,6 +109,40 @@ void LabelObjectOpeningMuParserFilter<TImage, TFunction>::SetAttributes(std::vec this->m_Functor.SetAttributes(shapeAttributes, statAttributes, nbOfBands); } +template < class TImage, class TFunction> +void LabelObjectOpeningMuParserFilter<TImage, TFunction>::GenerateInputRequestedRegion() +{ + ImagePointer input = const_cast<ImageType *>(this->GetInput()); + + if ( !input ) + { return; } + + for (unsigned int idx = 0; idx < this->GetNumberOfInputs(); ++idx) + { + ImagePointer input = const_cast<ImageType *>(this->GetInput(idx)); + if (!input.IsNull()) + { + input->SetRequestedRegionToLargestPossibleRegion(); + // Check whether the input is an image of the appropriate + // dimension (use ProcessObject's version of the GetInput() + // method since it returns the input as a pointer to a + // DataObject as opposed to the subclass version which + // static_casts the input to an TInputImage). + + // Use the function object RegionCopier to copy the output region + // to the input. The default region copier has default implementations + // to handle the cases where the input and output are the same + // dimension, the input a higher dimension than the output, and the + // input a lower dimension than the output. + InputImageRegionType inputRegion; + this->CallCopyOutputRegionToInputRegion(inputRegion, this->GetOutput()->GetRequestedRegion()); + input->SetRequestedRegion( inputRegion ); + } + } +} + + + template < class TImage, class TFunction> void LabelObjectOpeningMuParserFilter<TImage, TFunction>::GenerateData() { diff --git a/Code/BasicFilters/otbRelabelComponentImageFilter.h b/Code/BasicFilters/otbRelabelComponentImageFilter.h new file mode 100644 index 0000000000..04036749be --- /dev/null +++ b/Code/BasicFilters/otbRelabelComponentImageFilter.h @@ -0,0 +1,306 @@ +/*========================================================================= + + Program: Insight Segmentation & Registration Toolkit + Module: $RCSfile: itkRelabelComponentImageFilter.h,v $ + Language: C++ + Date: $Date: 2009-04-27 22:58:48 $ + Version: $Revision: 1.17 $ + + Copyright (c) Insight Software Consortium. All rights reserved. + See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#ifndef __otbRelabelComponentImageFilter_h +#define __otbRelabelComponentImageFilter_h + +#include "itkInPlaceImageFilter.h" +#include "itkImage.h" +#include <vector> + +namespace otb +{ + +/** + * \class RelabelComponentImageFilter + * \brief Relabel the components in an image such that consecutive labels are used. + * + * RelabelComponentImageFilter remaps the labels associated with the + * objects in an image (as from the output of + * ConnectedComponentImageFilter) such that the label numbers are + * consecutive with no gaps between the label numbers used. By + * default, the relabeling will also sort the labels based on the size + * of the object: the largest object will have label #1, the second + * largest will have label #2, etc. + * + * Label #0 is assumed to be background is left unaltered by the + * relabeling. + * + * RelabelComponentImageFilter is typically used on the output of the + * ConnectedComponentImageFilter for those applications that want to + * extract the largest object or the "k" largest objects. Any + * particular object can be extracted from the relabeled output using + * a BinaryThresholdImageFilter. A group of objects can be extracted + * from the relabled output using a ThresholdImageFilter. + * + * Once all the objects are relabeled, the application can query the + * number of objects and the size of each object. Object sizes are + * returned in a vector. The size of the background is not + * calculated. So the size of object #1 is + * GetSizeOfObjectsInPixels()[0], the size of object #2 is + * GetSizeOfObjectsInPixels()[1], etc. + * + * If user sets a minimum object size, all objects with fewer pixelss + * than the minimum will be discarded, so that the number of objects + * reported will be only those remaining. The + * GetOriginalNumberOfObjects method can be called to find out how + * many objects were present before the small ones were discarded. + * + * RelabelComponentImageFilter can be run as an "in place" filter, + * where it will overwrite its output. The default is run out of + * place (or generate a separate output). "In place" operation can be + * controlled via methods in the superclass, + * InPlaceImageFilter::InPlaceOn() and InPlaceImageFilter::InPlaceOff(). + * + * \sa ConnectedComponentImageFilter, BinaryThresholdImageFilter, ThresholdImageFilter + * + * \ingroup Singlethreaded + */ + +template <class TInputImage, class TOutputImage> +class ITK_EXPORT RelabelComponentImageFilter : + public itk::InPlaceImageFilter< TInputImage, TOutputImage > +{ +public: + /** + * Standard "Self" & Superclass typedef. + */ + typedef RelabelComponentImageFilter Self; + typedef itk::InPlaceImageFilter< TInputImage, TOutputImage > Superclass; + + /** + * Types from the Superclass + */ + typedef typename Superclass::InputImagePointer InputImagePointer; + + /** + * Extract some information from the image types. Dimensionality + * of the two images is assumed to be the same. + */ + typedef typename TOutputImage::PixelType OutputPixelType; + typedef typename TOutputImage::InternalPixelType OutputInternalPixelType; + typedef typename TInputImage::PixelType InputPixelType; + typedef typename TInputImage::InternalPixelType InputInternalPixelType; + itkStaticConstMacro(ImageDimension, unsigned int, + TOutputImage::ImageDimension); + itkStaticConstMacro(InputImageDimension, unsigned int, + TInputImage::ImageDimension); + + /** + * Image typedef support + */ + typedef TInputImage InputImageType; + typedef TOutputImage OutputImageType; + typedef typename TInputImage::IndexType IndexType; + typedef typename TInputImage::SizeType SizeType; + typedef typename TOutputImage::RegionType RegionType; + + /** + * Smart pointer typedef support + */ + typedef itk::SmartPointer<Self> Pointer; + typedef itk::SmartPointer<const Self> ConstPointer; + + /** + * Run-time type information (and related methods) + */ + itkTypeMacro(RelabelComponentImageFilter, ImageToImageFilter); + + /** + * Method for creation through the object factory. + */ + itkNewMacro(Self); + + /** Type used as identifier for the different component lables. */ + typedef unsigned long int LabelType; + + /** Type used to count number of pixels in objects. */ + typedef unsigned long int ObjectSizeType; + + /** Get the number of objects in the image. This information is only + * valid after the filter has executed. */ + itkGetConstMacro(NumberOfObjects, LabelType); + + /** Get the original number of objects in the image before small + * objects were discarded. This information is only valid after + * the filter has executed. If the caller has not specified a + * minimum object size, OriginalNumberOfObjects is the same as + * NumberOfObjects. */ + itkGetConstMacro(OriginalNumberOfObjects, LabelType); + + /** Get/Set the number of objects enumerated and described when the + * filter is printed. */ + itkSetMacro(NumberOfObjectsToPrint, LabelType); + itkGetConstReferenceMacro(NumberOfObjectsToPrint, LabelType); + + /** Set the minimum size in pixels for an object. All objects + * smaller than this size will be discarded and will not appear + * in the output label map. NumberOfObjects will count only the + * objects whose pixel counts are greater than or equal to the + * minimum size. Call GetOriginalNumberOfObjects to find out how + * many objects were present in the original label map. */ + itkSetMacro(MinimumObjectSize, ObjectSizeType); + + /** Get the caller-defined minimum size of an object in pixels. + * If the caller has not set the minimum, 0 will be returned, + * which is to be interpreted as meaning that no minimum exists, + * and all objects in the original label map will be passed + * through to the output. */ + itkGetConstMacro(MinimumObjectSize, ObjectSizeType); + + /** Get the size of each object in pixels. This information is only + * valid after the filter has executed. Size of the background is + * not calculated. Size of object #1 is + * GetSizeOfObjectsInPixels()[0]. Size of object #2 is + * GetSizeOfObjectsInPixels()[1]. Etc. */ + const std::vector<ObjectSizeType>& GetSizeOfObjectsInPixels() const + { return m_SizeOfObjectsInPixels; } + + /** Get the size of each object in physical space (in units of pixel + * size). This information is only valid after the filter has + * executed. Size of the background is not calculated. Size of + * object #1 is GetSizeOfObjectsInPhysicalUnits()[0]. Size of object + * #2 is GetSizeOfObjectsInPhysicalUnits()[1]. Etc. */ + const std::vector<float>& GetSizeOfObjectsInPhysicalUnits() const + { return m_SizeOfObjectsInPhysicalUnits; } + + /** Get the size of a particular object in pixels. This information is only + * valid after the filter has executed. Size of the background + * (object #0) is not calculated. */ + ObjectSizeType GetSizeOfObjectInPixels( LabelType obj ) const + { + if (obj > 0 && obj <= m_NumberOfObjects) + { + return m_SizeOfObjectsInPixels[obj-1]; + } + else + { + return 0; + } + } + + /** Get the size of a particular object in physical space (in units of pixel + * size). This information is only valid after the filter has + * executed. Size of the background (object #0) is not calculated. */ + float GetSizeOfObjectInPhysicalUnits( LabelType obj ) const + { + if (obj > 0 && obj <= m_NumberOfObjects) + { + return m_SizeOfObjectsInPhysicalUnits[obj-1]; + } + else + { + return 0; + } + } + +#ifdef ITK_USE_CONCEPT_CHECKING + /** Begin concept checking */ + itkConceptMacro(InputEqualityComparableCheck, + (itk::Concept::EqualityComparable<InputPixelType>)); + itkConceptMacro(UnsignedLongConvertibleToInputCheck, + (itk::Concept::Convertible<LabelType, InputPixelType>)); + itkConceptMacro(OutputLongConvertibleToUnsignedLongCheck, + (itk::Concept::Convertible<OutputPixelType, LabelType>)); + itkConceptMacro(InputConvertibleToOutputCheck, + (itk::Concept::Convertible<InputPixelType, OutputPixelType>)); + itkConceptMacro(SameDimensionCheck, + (itk::Concept::SameDimension<InputImageDimension, ImageDimension>)); + /** End concept checking */ +#endif + +protected: + + RelabelComponentImageFilter() + : m_NumberOfObjects(0), m_NumberOfObjectsToPrint(10), + m_OriginalNumberOfObjects(0), m_MinimumObjectSize(0) + { this->InPlaceOff(); } + virtual ~RelabelComponentImageFilter() {} + RelabelComponentImageFilter(const Self&) {} + + /** + * Standard pipeline method. + */ + void GenerateData(); + + /** RelabelComponentImageFilter needs the entire input. Therefore + * it must provide an implementation GenerateInputRequestedRegion(). + * \sa ProcessObject::GenerateInputRequestedRegion(). */ + void GenerateInputRequestedRegion(); + + /** + * Initialize parameters. + */ + void Init(); + + /** Standard printself method */ + void PrintSelf(std::ostream& os, itk::Indent indent) const; + + struct RelabelComponentObjectType + { + LabelType m_ObjectNumber; + ObjectSizeType m_SizeInPixels; + float m_SizeInPhysicalUnits; + }; + + // put the function objects here for sorting in descending order + class RelabelComponentSizeInPixelsComparator + { + public: + bool operator()(const RelabelComponentObjectType&a, + const RelabelComponentObjectType &b) + { + if (a.m_SizeInPixels > b.m_SizeInPixels) + { + return true; + } + else if (a.m_SizeInPixels < b.m_SizeInPixels) + { + return false; + } + // size in pixels and physical units are the same, sort based on + // original object number + else if (a.m_ObjectNumber < b.m_ObjectNumber) + { + return true; + } + else + { + return false; + } + } + }; + + +private: + + LabelType m_NumberOfObjects; + LabelType m_NumberOfObjectsToPrint; + LabelType m_OriginalNumberOfObjects; + ObjectSizeType m_MinimumObjectSize; + + std::vector<ObjectSizeType> m_SizeOfObjectsInPixels; + std::vector<float> m_SizeOfObjectsInPhysicalUnits; + +}; + +} // end namespace otb + +#ifndef ITK_MANUAL_INSTANTIATION +#include "otbRelabelComponentImageFilter.txx" +#endif + +#endif diff --git a/Code/BasicFilters/otbRelabelComponentImageFilter.txx b/Code/BasicFilters/otbRelabelComponentImageFilter.txx new file mode 100644 index 0000000000..f106cdb35d --- /dev/null +++ b/Code/BasicFilters/otbRelabelComponentImageFilter.txx @@ -0,0 +1,259 @@ +/*========================================================================= + + Program: Insight Segmentation & Registration Toolkit + Module: $RCSfile: itkRelabelComponentImageFilter.txx,v $ + Language: C++ + Date: $Date: 2009-04-27 22:58:48 $ + Version: $Revision: 1.18 $ + + Copyright (c) Insight Software Consortium. All rights reserved. + See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#ifndef __otbRelabelComponentImageFilter_txx +#define __otbRelabelComponentImageFilter_txx + +#include "otbRelabelComponentImageFilter.h" +#include "itkImageRegionIterator.h" +#include "itkImageRegionConstIterator.h" +#include "itkNumericTraits.h" +#include "itkProgressReporter.h" +#include "itk_hash_map.h" +#include <map> + +namespace otb +{ + +template< class TInputImage, class TOutputImage > +void +RelabelComponentImageFilter< TInputImage, TOutputImage > +::GenerateInputRequestedRegion() +{ + + itk::ImageToImageFilter<TInputImage, TOutputImage>::GenerateInputRequestedRegion(); +} + + +template< class TInputImage, class TOutputImage > +void +RelabelComponentImageFilter< TInputImage, TOutputImage > +::GenerateData() +{ + unsigned long i; + + // Use a map to keep track of the size of each object. Object + // number -> ObjectType (which has Object number and the two sizes) + typedef itk::hash_map<LabelType, RelabelComponentObjectType> MapType; + MapType sizeMap; + typename MapType::iterator mapIt; + typedef typename MapType::value_type MapValueType; + + // Get the input and the output + typename TInputImage::ConstPointer input = this->GetInput(); + typename TOutputImage::Pointer output = this->GetOutput(); + + // Setup a progress reporter. We have 2 stages to the algorithm so + // use the total number of pixels accessed. We walk the entire input + // in the first pass, then walk just the output requested region in + // the second pass. + itk::ProgressReporter progress(this, 0, + input->GetRequestedRegion().GetNumberOfPixels() + + output->GetRequestedRegion().GetNumberOfPixels()); + + + // Calculate the size of pixel + float physicalPixelSize = 1.0; + for (i=0; i < TInputImage::ImageDimension; ++i) + { + physicalPixelSize *= input->GetSpacing()[i]; + } + + RelabelComponentObjectType initialSize; + initialSize.m_SizeInPixels = 1; + initialSize.m_SizeInPhysicalUnits = physicalPixelSize; + + // First pass: walk the entire input image and determine what + // labels are used and the number of pixels used in each label. + // + + // walk the input + itk::ImageRegionConstIterator<InputImageType> it(input, input->GetRequestedRegion()); + it.GoToBegin(); + + while (!it.IsAtEnd()) + { + // Get the input pixel value + const LabelType inputValue = static_cast< LabelType>( it.Get() ); + + // if the input pixel is not the background + if (inputValue != itk::NumericTraits<LabelType>::Zero) + { + // Does this label already exist + mapIt = sizeMap.find( inputValue ); + if ( mapIt == sizeMap.end() ) + { + // label is not currently in the map + initialSize.m_ObjectNumber = inputValue; + sizeMap.insert( MapValueType( inputValue, initialSize ) ); + } + else + { + // label is already in the map, update the values + (*mapIt).second.m_SizeInPixels++; + (*mapIt).second.m_SizeInPhysicalUnits += physicalPixelSize; + } + } + + // increment the iterators + ++it; + progress.CompletedPixel(); + } + + // Now we need to reorder the labels. Use the m_ObjectSortingOrder + // to determine how to sort the objects. Define a map for converting + // input labels to output labels. + // + typedef std::vector<RelabelComponentObjectType> VectorType; + VectorType sizeVector; + typename VectorType::iterator vit; + + typedef std::map< LabelType, LabelType > RelabelMapType; + RelabelMapType relabelMap; + + // copy the original object map to a vector so we can sort it + for (mapIt = sizeMap.begin(); mapIt != sizeMap.end(); ++mapIt) + { + sizeVector.push_back( (*mapIt).second ); + } + + // sort the objects by size and define the map to use to relabel the image + std::sort(sizeVector.begin(), sizeVector.end(), RelabelComponentSizeInPixelsComparator() ); + + // create a lookup table to map the input label to the output label. + // cache the object sizes for later access by the user + m_NumberOfObjects = sizeVector.size(); + m_OriginalNumberOfObjects = sizeVector.size(); + m_SizeOfObjectsInPixels.clear(); + m_SizeOfObjectsInPixels.resize(m_NumberOfObjects); + m_SizeOfObjectsInPhysicalUnits.clear(); + m_SizeOfObjectsInPhysicalUnits.resize(m_NumberOfObjects); + int NumberOfObjectsRemoved = 0; + for (i=0, vit = sizeVector.begin(); vit != sizeVector.end(); ++vit, ++i) + { + + // if we find an object smaller than the minimum size, we + // terminate the loop. + if (m_MinimumObjectSize > 0 && (*vit).m_SizeInPixels < m_MinimumObjectSize) + { + // map small objects to the background + NumberOfObjectsRemoved++; + relabelMap.insert(RelabelMapType::value_type( (*vit).m_ObjectNumber, 0)); + } + else + { + // map for input labels to output labels (Note we use i+1 in the + // map since index 0 is the background) + relabelMap.insert(RelabelMapType::value_type( (*vit).m_ObjectNumber, i+1)); + + // cache object sizes for later access by the user + m_SizeOfObjectsInPixels[i] = (*vit).m_SizeInPixels; + m_SizeOfObjectsInPhysicalUnits[i] = (*vit).m_SizeInPhysicalUnits; + } + + } + + // update number of objects and resize cache vectors if we have removed small objects + m_NumberOfObjects -= NumberOfObjectsRemoved; + if (NumberOfObjectsRemoved > 0) + { + m_SizeOfObjectsInPixels.resize(m_NumberOfObjects); + m_SizeOfObjectsInPhysicalUnits.resize(m_NumberOfObjects); + } + + // Second pass: walk just the output requested region and relabel + // the necessary pixels. + // + + // Allocate the output + this->AllocateOutputs(); + + // Remap the labels. Note we only walk the region of the output + // that was requested. This may be a subset of the input image. + OutputPixelType outputValue; + itk::ImageRegionIterator<OutputImageType> oit; + oit = itk::ImageRegionIterator<OutputImageType>(output, + output->GetRequestedRegion()); + it = itk::ImageRegionConstIterator<InputImageType>(input, + output->GetRequestedRegion()); + + it.GoToBegin(); + oit.GoToBegin(); + while ( !oit.IsAtEnd() ) + { + const LabelType inputValue = static_cast< LabelType>( it.Get() ); + + if (inputValue != itk::NumericTraits<LabelType>::Zero) + { + + // lookup the mapped label + outputValue = static_cast<OutputPixelType>(relabelMap[inputValue]); + oit.Set( outputValue ); + } + else + { + oit.Set( inputValue ); + } + + // increment the iterators + ++it; + ++oit; + progress.CompletedPixel(); + } +} + + +template< class TInputImage, class TOutputImage > +void +RelabelComponentImageFilter< TInputImage, TOutputImage > +::PrintSelf(std::ostream& os, itk::Indent indent) const +{ + Superclass::PrintSelf(os, indent); + + os << indent << "NumberOfObjects: " << m_NumberOfObjects << std::endl; + os << indent << "OriginalNumberOfObjects: " << m_OriginalNumberOfObjects << std::endl; + os << indent << "NumberOfObjectsToPrint: " + << m_NumberOfObjectsToPrint << std::endl; + os << indent << "MinimumObjectSizez: " << m_MinimumObjectSize << std::endl; + + std::vector<ObjectSizeType>::const_iterator it; + std::vector<float>::const_iterator fit; + LabelType i; + + // limit the number of objects to print + LabelType numPrint = m_NumberOfObjectsToPrint; + if (numPrint > m_SizeOfObjectsInPixels.size()) + { + numPrint = m_SizeOfObjectsInPixels.size(); + } + + + for (i=0, it = m_SizeOfObjectsInPixels.begin(), + fit = m_SizeOfObjectsInPhysicalUnits.begin(); + i < numPrint; ++it, ++fit, ++i) + { + os << indent << "Object #" << i+1 << ": " << *it << " pixels, " + << *fit << " physical units" << std::endl; + } + if (numPrint < m_SizeOfObjectsInPixels.size()) + { + os << indent << "..." << std::endl; + } +} + +} // end namespace itk + +#endif diff --git a/Code/OBIA/otbBandsStatisticsAttributesLabelMapFilter.h b/Code/OBIA/otbBandsStatisticsAttributesLabelMapFilter.h index eb510a4de3..501e230e82 100644 --- a/Code/OBIA/otbBandsStatisticsAttributesLabelMapFilter.h +++ b/Code/OBIA/otbBandsStatisticsAttributesLabelMapFilter.h @@ -138,6 +138,7 @@ class ITK_EXPORT BandsStatisticsAttributesLabelMapFilter public: /** Some convenient typedefs. */ typedef TImage ImageType; + typedef typename ImageType::RegionType InputImageRegionType; typedef typename ImageType::LabelObjectType LabelObjectType; typedef TFeatureImage FeatureImageType; typedef typename FeatureImageType::InternalPixelType FeatureInternalPixelType; @@ -154,6 +155,7 @@ public: <ImageType, FunctorType> Superclass; typedef itk::SmartPointer<Self> Pointer; typedef itk::SmartPointer<const Self> ConstPointer; + typedef typename ImageType::Pointer ImagePointer; /** ImageDimension constants */ itkStaticConstMacro(ImageDimension, unsigned int, TImage::ImageDimension); @@ -184,6 +186,10 @@ protected: /** Destructor */ ~BandsStatisticsAttributesLabelMapFilter() {} + void GenerateInputRequestedRegion(); + + void EnlargeOutputRequestedRegion(itk::DataObject *){}; + /** Before threaded data generation */ virtual void BeforeThreadedGenerateData(); diff --git a/Code/OBIA/otbBandsStatisticsAttributesLabelMapFilter.txx b/Code/OBIA/otbBandsStatisticsAttributesLabelMapFilter.txx index 77fe216133..5309e0ff66 100644 --- a/Code/OBIA/otbBandsStatisticsAttributesLabelMapFilter.txx +++ b/Code/OBIA/otbBandsStatisticsAttributesLabelMapFilter.txx @@ -210,6 +210,39 @@ BandsStatisticsAttributesLabelMapFilter<TImage, TFeatureImage> return this->GetFunctor().GetReducedAttributeSet(); } +template <class TImage, class TFeatureImage> +void +BandsStatisticsAttributesLabelMapFilter<TImage, TFeatureImage> +::GenerateInputRequestedRegion() +{ + + for (unsigned int idx = 0; idx < this->GetNumberOfInputs(); ++idx) + { + ImagePointer input = const_cast<ImageType *>(this->GetInput(idx)); + if (!input.IsNull()) + { + input->SetRequestedRegionToLargestPossibleRegion(); + // Check whether the input is an image of the appropriate + // dimension (use ProcessObject's version of the GetInput() + // method since it returns the input as a pointer to a + // DataObject as opposed to the subclass version which + // static_casts the input to an TInputImage). + + // Use the function object RegionCopier to copy the output region + // to the input. The default region copier has default implementations + // to handle the cases where the input and output are the same + // dimension, the input a higher dimension than the output, and the + // input a lower dimension than the output. + InputImageRegionType inputRegion; + this->CallCopyOutputRegionToInputRegion(inputRegion, this->GetOutput()->GetRequestedRegion()); + input->SetRequestedRegion( inputRegion ); + } + } +} + + + + template <class TImage, class TFeatureImage> void BandsStatisticsAttributesLabelMapFilter<TImage, TFeatureImage> diff --git a/Code/OBIA/otbLabelImageToLabelMapWithAdjacencyFilter.txx b/Code/OBIA/otbLabelImageToLabelMapWithAdjacencyFilter.txx index 9dfc10494c..c6fef88335 100644 --- a/Code/OBIA/otbLabelImageToLabelMapWithAdjacencyFilter.txx +++ b/Code/OBIA/otbLabelImageToLabelMapWithAdjacencyFilter.txx @@ -40,14 +40,28 @@ void LabelImageToLabelMapWithAdjacencyFilter<TInputImage, TOutputImage> ::GenerateInputRequestedRegion() { - // call the superclass' implementation of this method - Superclass::GenerateInputRequestedRegion(); - - // We need all the input. - InputImagePointer input = const_cast<InputImageType *>(this->GetInput()); - if ( !input ) - { return; } - input->SetRequestedRegion( input->GetLargestPossibleRegion() ); + for (unsigned int idx = 0; idx < this->GetNumberOfInputs(); ++idx) + { + InputImagePointer input = const_cast<InputImageType *>(this->GetInput(idx)); + if (!input.IsNull()) + { + input->SetRequestedRegionToLargestPossibleRegion(); + // Check whether the input is an image of the appropriate + // dimension (use ProcessObject's version of the GetInput() + // method since it returns the input as a pointer to a + // DataObject as opposed to the subclass version which + // static_casts the input to an TInputImage). + + // Use the function object RegionCopier to copy the output region + // to the input. The default region copier has default implementations + // to handle the cases where the input and output are the same + // dimension, the input a higher dimension than the output, and the + // input a lower dimension than the output. + InputImageRegionType inputRegion; + this->CallCopyOutputRegionToInputRegion(inputRegion, this->GetOutput()->GetRequestedRegion()); + input->SetRequestedRegion( inputRegion ); + } + } } @@ -56,8 +70,6 @@ void LabelImageToLabelMapWithAdjacencyFilter<TInputImage, TOutputImage> ::EnlargeOutputRequestedRegion(itk::DataObject *) { - this->GetOutput() - ->SetRequestedRegion( this->GetOutput()->GetLargestPossibleRegion() ); } diff --git a/Code/OBIA/otbLabelMapToLabelImageFilter.h b/Code/OBIA/otbLabelMapToLabelImageFilter.h new file mode 100644 index 0000000000..d67db14f6b --- /dev/null +++ b/Code/OBIA/otbLabelMapToLabelImageFilter.h @@ -0,0 +1,95 @@ +/*========================================================================= + + Program: Insight Segmentation & Registration Toolkit + Module: $RCSfile: itkLabelMapToLabelImageFilter.h,v $ + Language: C++ + Date: $Date: 2009-07-30 22:54:24 $ + Version: $Revision: 1.3 $ + + Copyright (c) Insight Software Consortium. All rights reserved. + See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#ifndef __otbLabelMapToLabelImageFilter_h +#define __otbLabelMapToLabelImageFilter_h + +#include "itkLabelMapToLabelImageFilter.h" + +namespace otb { + +/** \class LabelMapToLabelImageFilter + * \brief Converts a LabelMap to a labeled image. + * + * LabelMapToBinaryImageFilter to a label image. + * + * \author Gaetan Lehmann. Biologie du Developpement et de la Reproduction, INRA de Jouy-en-Josas, France. + * + * This implementation was taken from the Insight Journal paper: + * http://hdl.handle.net/1926/584 or + * http://www.insight-journal.org/browse/publication/176 + * + * \sa LabelMapToBinaryImageFilter, LabelMapMaskImageFilter + * \ingroup ImageEnhancement MathematicalMorphologyImageFilters + * \ingroup LabeledImageFilters + */ +template<class TInputImage, class TOutputImage> +class ITK_EXPORT LabelMapToLabelImageFilter : + public itk::LabelMapToLabelImageFilter<TInputImage, TOutputImage> +{ +public: + /** Standard class typedefs. */ + typedef LabelMapToLabelImageFilter Self; + typedef itk::LabelMapToLabelImageFilter<TInputImage, TOutputImage> Superclass; + typedef itk::SmartPointer<Self> Pointer; + typedef itk::SmartPointer<const Self> ConstPointer; + + /** Some convenient typedefs. */ + typedef TInputImage InputImageType; + typedef TOutputImage OutputImageType; + typedef typename InputImageType::Pointer InputImagePointer; + typedef typename InputImageType::ConstPointer InputImageConstPointer; + typedef typename InputImageType::RegionType InputImageRegionType; + typedef typename InputImageType::PixelType InputImagePixelType; + typedef typename InputImageType::LabelObjectType LabelObjectType; + + typedef typename OutputImageType::Pointer OutputImagePointer; + typedef typename OutputImageType::ConstPointer OutputImageConstPointer; + typedef typename OutputImageType::RegionType OutputImageRegionType; + typedef typename OutputImageType::PixelType OutputImagePixelType; + typedef typename OutputImageType::IndexType IndexType; + + /** ImageDimension constants */ + itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension); + itkStaticConstMacro(OutputImageDimension, unsigned int, TOutputImage::ImageDimension); + + /** Standard New method. */ + itkNewMacro(Self); + + /** Runtime information support. */ + itkTypeMacro(LabelMapToLabelImageFilter, ImageToImageFilter); + +protected: + LabelMapToLabelImageFilter(); + ~LabelMapToLabelImageFilter() {}; + + void GenerateInputRequestedRegion(); + + void EnlargeOutputRequestedRegion(itk::DataObject *){}; + +private: + LabelMapToLabelImageFilter(const Self&); //purposely not implemented + void operator=(const Self&); //purposely not implemented + +}; // end of class + +} // end namespace otb + +#ifndef ITK_MANUAL_INSTANTIATION +#include "otbLabelMapToLabelImageFilter.txx" +#endif + +#endif diff --git a/Code/OBIA/otbLabelMapToLabelImageFilter.txx b/Code/OBIA/otbLabelMapToLabelImageFilter.txx new file mode 100644 index 0000000000..719438d048 --- /dev/null +++ b/Code/OBIA/otbLabelMapToLabelImageFilter.txx @@ -0,0 +1,69 @@ +/*========================================================================= + + Program: Insight Segmentation & Registration Toolkit + Module: $RCSfile: itkLabelMapToLabelImageFilter.txx,v $ + Language: C++ + Date: $Date: 2009-05-16 22:19:31 $ + Version: $Revision: 1.2 $ + + Copyright (c) Insight Software Consortium. All rights reserved. + See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#ifndef __otbLabelMapToLabelImageFilter_txx +#define __otbLabelMapToLabelImageFilter_txx + +#include "otbLabelMapToLabelImageFilter.h" +#include "itkNumericTraits.h" +#include "itkProgressReporter.h" +#include "itkImageRegionConstIteratorWithIndex.h" + +namespace otb { + +template <class TInputImage, class TOutputImage> +LabelMapToLabelImageFilter<TInputImage, TOutputImage> +::LabelMapToLabelImageFilter() +{ +} + +template<class TInputImage, class TOutputImage> +void +LabelMapToLabelImageFilter<TInputImage, TOutputImage> +::GenerateInputRequestedRegion() +{ + + + for (unsigned int idx = 0; idx < this->GetNumberOfInputs(); ++idx) + { + InputImagePointer input = const_cast<InputImageType *>(this->GetInput(idx)); + if (!input.IsNull()) + { + input->SetRequestedRegionToLargestPossibleRegion(); + // Check whether the input is an image of the appropriate + // dimension (use ProcessObject's version of the GetInput() + // method since it returns the input as a pointer to a + // DataObject as opposed to the subclass version which + // static_casts the input to an TInputImage). + + // Use the function object RegionCopier to copy the output region + // to the input. The default region copier has default implementations + // to handle the cases where the input and output are the same + // dimension, the input a higher dimension than the output, and the + // input a lower dimension than the output. + InputImageRegionType inputRegion; + this->CallCopyOutputRegionToInputRegion(inputRegion, this->GetOutput()->GetRequestedRegion()); + input->SetRequestedRegion( inputRegion ); + } + } + + +} + + + +}// end namespace otb +#endif diff --git a/Code/OBIA/otbShapeAttributesLabelMapFilter.h b/Code/OBIA/otbShapeAttributesLabelMapFilter.h index 0e7c514283..ed4b358604 100644 --- a/Code/OBIA/otbShapeAttributesLabelMapFilter.h +++ b/Code/OBIA/otbShapeAttributesLabelMapFilter.h @@ -81,6 +81,13 @@ public: /** Get the compute perimeter flag */ bool GetComputePerimeter() const; + /** Set the polygonalisation flag */ + void SetComputePolygon(bool flag); + + /** Get the polygonalisation flag */ + bool GetComputePolygon() const; + + /** Set the compute feret diameter flag */ void SetComputeFeretDiameter(bool flag); @@ -130,9 +137,12 @@ private: /** Do we compute the feret diameter ? */ bool m_ComputeFeretDiameter; - /** Du we compute the perimeter ? */ + /** Do we compute the perimeter ? */ bool m_ComputePerimeter; + /** Do we polygonise ? */ + bool m_ComputePolygon; + /** Compute only a reduced attribute set */ bool m_ReducedAttributeSet; @@ -181,6 +191,7 @@ public: /** Template parameters typedefs */ typedef TImage ImageType; typedef typename ImageType::LabelObjectType LabelObjectType; + typedef typename ImageType::RegionType InputImageRegionType; typedef TLabelImage LabelImageType; typedef Functor::ShapeAttributesLabelObjectFunctor <LabelObjectType, LabelImageType> FunctorType; @@ -193,6 +204,7 @@ public: typedef itk::SmartPointer<Self> Pointer; typedef itk::SmartPointer<const Self> ConstPointer; + typedef typename ImageType::Pointer ImagePointer; /** ImageDimension constants */ itkStaticConstMacro(ImageDimension, unsigned int, TImage::ImageDimension); @@ -218,6 +230,14 @@ public: bool GetComputePerimeter() const; itkBooleanMacro(ComputePerimeter); + /** + * Set/Get whether the polygonalisation process should be computed or not. The defaut value + * is true, to assure backward compatibility. + */ + void SetComputePolygon(bool flag); + bool GetComputePolygon() const; + itkBooleanMacro(ComputePolygon); + /** Set/get the ReducedAttributesSet flag */ void SetReducedAttributeSet(bool flag); bool GetReducedAttributeSet() const; @@ -241,6 +261,10 @@ protected: /** Things to to before threaded data generation */ virtual void BeforeThreadedGenerateData(); + void GenerateInputRequestedRegion(); + + void EnlargeOutputRequestedRegion(itk::DataObject *){}; + private: ShapeAttributesLabelMapFilter(const Self &); //purposely not implemented void operator =(const Self&); //purposely not implemented diff --git a/Code/OBIA/otbShapeAttributesLabelMapFilter.txx b/Code/OBIA/otbShapeAttributesLabelMapFilter.txx index b36acc9260..fc8615dc49 100644 --- a/Code/OBIA/otbShapeAttributesLabelMapFilter.txx +++ b/Code/OBIA/otbShapeAttributesLabelMapFilter.txx @@ -40,6 +40,7 @@ template <class TLabelObject, class TLabelImage> ShapeAttributesLabelObjectFunctor<TLabelObject, TLabelImage> ::ShapeAttributesLabelObjectFunctor() : m_ComputeFeretDiameter(false), m_ComputePerimeter(false), + m_ComputePolygon(true), m_ReducedAttributeSet(true), m_PerimeterCalculator(NULL), m_LabelImage(NULL) @@ -92,6 +93,27 @@ ShapeAttributesLabelObjectFunctor<TLabelObject, TLabelImage> return m_ComputePerimeter; } +/** Set the compute perimeter flag */ +template <class TLabelObject, class TLabelImage> +void +ShapeAttributesLabelObjectFunctor<TLabelObject, TLabelImage> +::SetComputePolygon(bool flag) +{ + m_ComputePolygon = flag; +} + +/** Get the compute perimeter flag */ +template <class TLabelObject, class TLabelImage> +bool +ShapeAttributesLabelObjectFunctor<TLabelObject, TLabelImage> +::GetComputePolygon() const +{ + return m_ComputePolygon; +} + + + + /** Set the compute feret diameter flag */ template <class TLabelObject, class TLabelImage> void @@ -499,13 +521,16 @@ ShapeAttributesLabelObjectFunctor<TLabelObject, TLabelImage> } // Set the attributes - PolygonFunctorType polygonFunctor; - SimplifyPolygonFunctorType simplifyFunctor; - polygonFunctor.SetStartIndex(m_LabelImage->GetLargestPossibleRegion().GetIndex()); - polygonFunctor.SetOrigin(m_LabelImage->GetOrigin()); - polygonFunctor.SetSpacing(m_LabelImage->GetSpacing()); - typename PolygonType::Pointer polygon = simplifyFunctor(polygonFunctor(lo)); - lo->SetPolygon(polygon); + if (m_ComputePolygon) + { + PolygonFunctorType polygonFunctor; + SimplifyPolygonFunctorType simplifyFunctor; + polygonFunctor.SetStartIndex(m_LabelImage->GetLargestPossibleRegion().GetIndex()); + polygonFunctor.SetOrigin(m_LabelImage->GetOrigin()); + polygonFunctor.SetSpacing(m_LabelImage->GetSpacing()); + typename PolygonType::Pointer polygon = simplifyFunctor(polygonFunctor(lo)); + lo->SetPolygon(polygon); + } // Physical size lo->SetAttribute("SHAPE::PhysicalSize", physicalSize); @@ -747,6 +772,27 @@ ShapeAttributesLabelMapFilter<TImage, TLabelImage> return this->GetFunctor().GetComputePerimeter(); } +template<class TImage, class TLabelImage> +void +ShapeAttributesLabelMapFilter<TImage, TLabelImage> +::SetComputePolygon(bool flag) +{ + if (this->GetFunctor().GetComputePolygon() != flag) + { + this->GetFunctor().SetComputePolygon(flag); + this->Modified(); + } +} + +template<class TImage, class TLabelImage> +bool +ShapeAttributesLabelMapFilter<TImage, TLabelImage> +::GetComputePolygon() const +{ + return this->GetFunctor().GetComputePolygon(); +} + + template<class TImage, class TLabelImage> void ShapeAttributesLabelMapFilter<TImage, TLabelImage> @@ -787,13 +833,45 @@ ShapeAttributesLabelMapFilter<TImage, TLabelImage> return this->GetFunctor().GetLabelImage(); } +template <class TImage, class TLabelImage> +void +ShapeAttributesLabelMapFilter<TImage, TLabelImage> +::GenerateInputRequestedRegion() +{ + + for (unsigned int idx = 0; idx < this->GetNumberOfInputs(); ++idx) + { + ImagePointer input = const_cast<ImageType *>(this->GetInput(idx)); + if (!input.IsNull()) + { + input->SetRequestedRegionToLargestPossibleRegion(); + // Check whether the input is an image of the appropriate + // dimension (use ProcessObject's version of the GetInput() + // method since it returns the input as a pointer to a + // DataObject as opposed to the subclass version which + // static_casts the input to an TInputImage). + + // Use the function object RegionCopier to copy the output region + // to the input. The default region copier has default implementations + // to handle the cases where the input and output are the same + // dimension, the input a higher dimension than the output, and the + // input a lower dimension than the output. + InputImageRegionType inputRegion; + this->CallCopyOutputRegionToInputRegion(inputRegion, this->GetOutput()->GetRequestedRegion()); + input->SetRequestedRegion( inputRegion ); + } + } + + +} + + template<class TImage, class TLabelImage> void ShapeAttributesLabelMapFilter<TImage, TLabelImage> ::BeforeThreadedGenerateData() { Superclass::BeforeThreadedGenerateData(); - if (!this->GetFunctor().GetLabelImage()) { // generate an image of the labelized image -- GitLab