/*========================================================================= * * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkMRASlabIdentifier_h #define itkMRASlabIdentifier_h #include "itkObject.h" #include "itkImage.h" #include namespace itk { /** * \class MRASlabIdentifier * \brief Identifies slabs in MR images comparing minimum intensity averages. * * This class is templated over the type of image. * In many cases, a 3D MR image is constructed by merging smaller 3D * blocks (slabs) which were acquired with different settings such as magnetic * settings and patient positions. Therefore, stripe like patterns with slabs * can be present in the resulting image. Such artifacts are called "slab * boundary" artifacts or "venetian blind" artifacts. * * Due to the slab boundary artifacts in an image, even same tissue class' * intensity values might vary significantly along the borders of slabs. * Such rough value changes are not appropriate for some image processing * methods. For example, MRIBiasFieldCorrectionFilter assumes a smooth bias * field. However, with the slab boundary artifacts, the bias field estimation * scheme that MRIBiasFieldCorrectionFilter uses might not adapt well. * The MRIBiasFieldCorrectionFilter creates regions for slabs using the * MRASlabIdentifier and then applies its bias correction scheme to each slab. * * For this identifier, a slice means 2D image data which is extracted from * the input image along one of three axes \f$(x, y, z)\f$. Users can specify * the slicing axis using the SetSlicingDirection(int dimension) member, where * the \p dimension variable follows the convention \f${X, Y, Z} : {0, 1, 2}\f$. * * The identification scheme used works according to the following steps: * -# Users should specify how many pixels per slice the identifier * will sample. * -# For each slice, the identifier searches the specified number of pixels * of which intensity values are greater than 0 and less than those * of the other pixels in the slice. * -# The identifier calculates the average for each slice and the overall * average using the search results. * -# For each slice, it subtracts the overall average from the slice average. * If the sign of the subtraction result changes, then it assumes that a * slab ends and another slab begins. * * \ingroup ITKBiasCorrection */ template class ITK_TEMPLATE_EXPORT MRASlabIdentifier : public Object { public: ITK_DISALLOW_COPY_AND_MOVE(MRASlabIdentifier); /** Standard class type aliases. */ using Self = MRASlabIdentifier; using Superclass = Object; using Pointer = SmartPointer; using ConstPointer = SmartPointer; /** \see LightObject::GetNameOfClass() */ itkOverrideGetNameOfClassMacro(MRASlabIdentifier); /** Method for creation through the object factory. */ itkNewMacro(Self); /** Type definition for the input image. */ using ImageType = TInputImage; /** Pointer type for the image. */ using ImagePointer = typename TInputImage::Pointer; /** Const Pointer type for the image. */ using ImageConstPointer = typename TInputImage::ConstPointer; /** Type definition for the input image pixel type. */ using ImagePixelType = typename TInputImage::PixelType; using ImageIndexType = typename TInputImage::IndexType; using ImageSizeType = typename TInputImage::SizeType; using ImageRegionType = typename TInputImage::RegionType; using SlabRegionVectorType = std::vector; /** Set/Get the input image. */ itkSetConstObjectMacro(Image, ImageType); itkGetConstObjectMacro(Image, ImageType); /** Set/Get the number of minimum intensity pixels per slice. */ itkSetMacro(NumberOfSamples, unsigned int); itkGetConstReferenceMacro(NumberOfSamples, unsigned int); /** Set/Get the minimum threshold value for the background pixels */ itkSetMacro(BackgroundMinimumThreshold, ImagePixelType); itkGetConstReferenceMacro(BackgroundMinimumThreshold, ImagePixelType); /** Set/Get the tolerance value. */ itkSetMacro(Tolerance, double); itkGetConstReferenceMacro(Tolerance, double); /** Set/Get the direction of slicing/ * 0 - x axis, 1 - y axis, 2 - z axis */ itkSetMacro(SlicingDirection, int); itkGetConstReferenceMacro(SlicingDirection, int); /** Compute the average values of minimum intensity pixels for each slice and * compare the average values with overall averages. */ void GenerateSlabRegions(); /** Get slab regions. */ SlabRegionVectorType GetSlabRegionVector(); protected: MRASlabIdentifier(); ~MRASlabIdentifier() override = default; void PrintSelf(std::ostream & os, Indent indent) const override; private: /** Target image pointer that MRASlabIdentifier will use. */ ImageConstPointer m_Image{}; /** The number of pixels per slice which will be included * for average calculation. In a sense, it's sampling size per slice. */ unsigned int m_NumberOfSamples{}; int m_SlicingDirection{}; ImagePixelType m_BackgroundMinimumThreshold{}; double m_Tolerance{}; SlabRegionVectorType m_Slabs{}; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION # include "itkMRASlabIdentifier.hxx" #endif #endif /* itkMRASlabIdentifier_h */