/*=========================================================================
 *
 *  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
 *
 *         http://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 <vector>

namespace itk
{
/**
 *\class MRASlabIdentifier
 * \brief identifies slab 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.
 *
 * With the slab boundary artifacts in an image, even a same tissue class's
 * 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 adopt well.
 * So, the MRIBiasFieldCorrectionFilter creates regions for slabs using the
 * MRASlabIdentifier and then apply 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 (x, y, z). Users can specify
 * the slicing axis using the SetSlicingDirection(int dimension) member.
 * (0 - x, 1 - y, 2 - z).
 *
 * The identification scheme used here is very simple.
 * 1) Users should specify how many pixels per slice the identifier
 *    will sample.
 * 2) 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
 * 3) The identifier calculates the average for each slice and the overall
 *    average using the search results.
 * 4) 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 <typename TInputImage>
class ITK_TEMPLATE_EXPORT MRASlabIdentifier : public Object
{
public:
  ITK_DISALLOW_COPY_AND_ASSIGN(MRASlabIdentifier);

  /** Standard class type aliases. */
  using Self = MRASlabIdentifier;
  using Superclass = Object;
  using Pointer = SmartPointer<Self>;
  using ConstPointer = SmartPointer<const Self>;

  /** Run-time type information (and related methods). */
  itkTypeMacro(MRASlabIdentifier, Object);

  /** 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<ImageRegionType>;

  /** 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 mininum 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 */
