/*=========================================================================
*
*  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 sitkN4BiasFieldCorrectionImageFilter_h
#define sitkN4BiasFieldCorrectionImageFilter_h

/*
 * WARNING: DO NOT EDIT THIS FILE!
 * THIS FILE IS AUTOMATICALLY GENERATED BY THE SIMPLEITK BUILD PROCESS.
 * Please look at sitkImageFilterTemplate.h.in to make changes.
 */

#include <memory>

#include "sitkBasicFilters.h"
#include "sitkImageFilter.h"

namespace itk::simple {

    /**\class N4BiasFieldCorrectionImageFilter
\brief Implementation of the N4 bias field correction algorithm.

The nonparametric nonuniform intensity normalization (N3) algorithm, as introduced by Sled et al. in 1998 is a method for correcting nonuniformity associated with MR images. The algorithm assumes a simple parametric model (Gaussian) for the bias field and does not require tissue class segmentation. In addition, there are only a couple of parameters to tune with the default values performing quite well. N3 has been publicly available as a set of perl scripts (https://www.bic.mni.mcgill.ca/ServicesSoftwareAdvancedImageProcessingTools/HomePage )

The N4 algorithm, encapsulated with this class, is a variation of the original N3 algorithm with the additional benefits of an improved B-spline fitting routine which allows for multiple resolutions to be used during the correction process. We also modify the iterative update component of algorithm such that the residual bias field is continually updated

Notes for the user:
\li Since much of the image manipulation is done in the log space of the intensities, input images with negative and small values (< 1) can produce poor results.

\li The original authors recommend performing the bias field correction on a downsampled version of the original image.

\li A binary mask or a weighted image can be supplied. If a binary mask is specified, those voxels in the input image which correspond to the voxels in the mask image are used to estimate the bias field. If a UseMaskLabel value is set to false (the default), all non-zero voxels in the MaskImage will be masked; otherwise only voxels in the MaskImage that match the MaskLabel will be used. If a confidence image is specified, the input voxels are weighted in the b-spline fitting routine according to the confidence voxel values.

\li The filter returns the corrected image. If the bias field is wanted, one can reconstruct it using the class itkBSplineControlPointImageFilter. See the IJ article and the test file for an example.

\li The 'Z' parameter in Sled's 1998 paper is the square root of the class variable 'm_WienerFilterNoise'.




The basic algorithm iterates between sharpening the intensity histogram of the corrected input image and spatially smoothing those results with a B-spline scalar field estimate of the bias field.

\author Nicholas J. Tustison


Contributed by Nicholas J. Tustison, James C. Gee in the Insight Journal paper: https://doi.org/10.54294/jculxw 

\par REFERENCE



J.G. Sled, A.P. Zijdenbos and A.C. Evans. "A Nonparametric Method for
Automatic Correction of Intensity Nonuniformity in Data" IEEE Transactions on Medical Imaging, Vol 17, No 1. Feb 1998.

N.J. Tustison, B.B. Avants, P.A. Cook, Y. Zheng, A. Egan, P.A. Yushkevich, and J.C. Gee. "N4ITK: Improved N3 Bias Correction" IEEE Transactions on Medical Imaging, 29(6):1310-1320, June 2010.
\sa itk::simple::N4BiasFieldCorrection for the procedural interface
\sa itk::N4BiasFieldCorrectionImageFilter for the Doxygen on the original ITK class.
     */
    class SITKBasicFilters_EXPORT N4BiasFieldCorrectionImageFilter : public ImageFilter {
    public:
      using Self = N4BiasFieldCorrectionImageFilter;

      /** Destructor */
      virtual ~N4BiasFieldCorrectionImageFilter();

      /** Default Constructor that takes no arguments and initializes
       * default parameters */
      N4BiasFieldCorrectionImageFilter();

      /** Define the pixels types supported by this filter */
      using PixelIDTypeList = RealPixelIDTypeList;
\

      /**
       * Set the convergence threshold. Convergence is determined by the coefficient of variation of the difference image between the current bias field estimate and the previous estimate. If this value is less than the specified threshold, the algorithm proceeds to the next fitting level or terminates if it is at the last level.
       */
      SITK_RETURN_SELF_TYPE_HEADER SetConvergenceThreshold ( double ConvergenceThreshold ) { this->m_ConvergenceThreshold = ConvergenceThreshold; return *this; }

      /**
       * Get the convergence threshold. Convergence is determined by the coefficient of variation of the difference image between the current bias field estimate and the previous estimate. If this value is less than the specified threshold, the algorithm proceeds to the next fitting level or terminates if it is at the last level.
       */
      double GetConvergenceThreshold() const { return this->m_ConvergenceThreshold; }\

      /**
       * Set the maximum number of iterations specified at each fitting level. Default = 50.
       */
      SITK_RETURN_SELF_TYPE_HEADER SetMaximumNumberOfIterations ( std::vector<uint32_t> MaximumNumberOfIterations ) { this->m_MaximumNumberOfIterations = MaximumNumberOfIterations; return *this; }

      /**
       * Get the maximum number of iterations specified at each fitting level. Default = 50.
       */
      std::vector<uint32_t> GetMaximumNumberOfIterations() const { return this->m_MaximumNumberOfIterations; }\

      /**
       * Set the full width at half maximum parameter characterizing the width of the Gaussian deconvolution. Default = 0.15.
       */
      SITK_RETURN_SELF_TYPE_HEADER SetBiasFieldFullWidthAtHalfMaximum ( double BiasFieldFullWidthAtHalfMaximum ) { this->m_BiasFieldFullWidthAtHalfMaximum = BiasFieldFullWidthAtHalfMaximum; return *this; }

      /**
       * Get the full width at half maximum parameter characterizing the width of the Gaussian deconvolution. Default = 0.15.
       */
      double GetBiasFieldFullWidthAtHalfMaximum() const { return this->m_BiasFieldFullWidthAtHalfMaximum; }\

      /**
       * Set the noise estimate defining the Wiener filter. Default = 0.01.
       */
      SITK_RETURN_SELF_TYPE_HEADER SetWienerFilterNoise ( double WienerFilterNoise ) { this->m_WienerFilterNoise = WienerFilterNoise; return *this; }

      /**
       * Get the noise estimate defining the Wiener filter. Default = 0.01.
       */
      double GetWienerFilterNoise() const { return this->m_WienerFilterNoise; }\

      /**
       * Set number of bins defining the log input intensity histogram. Default = 200.
       */
      SITK_RETURN_SELF_TYPE_HEADER SetNumberOfHistogramBins ( uint32_t NumberOfHistogramBins ) { this->m_NumberOfHistogramBins = NumberOfHistogramBins; return *this; }

      /**
       * Get number of bins defining the log input intensity histogram. Default = 200.
       */
      uint32_t GetNumberOfHistogramBins() const { return this->m_NumberOfHistogramBins; }\

      /**
       * Set the control point grid size defining the B-spline estimate of the scalar bias field. In each dimension, the B-spline mesh size is equal to the number of control points in that dimension minus the spline order. Default = 4 control points in each dimension for a mesh size of 1 in each dimension.
       */
      SITK_RETURN_SELF_TYPE_HEADER SetNumberOfControlPoints ( std::vector<uint32_t> NumberOfControlPoints ) { this->m_NumberOfControlPoints = std::move(NumberOfControlPoints); return *this; }

      /** Set the values of the NumberOfControlPoints vector all to value */
      SITK_RETURN_SELF_TYPE_HEADER SetNumberOfControlPoints( uint32_t value ) { this->m_NumberOfControlPoints = std::vector<uint32_t>(3, value); return *this; }

      /**
       * Get the control point grid size defining the B-spline estimate of the scalar bias field. In each dimension, the B-spline mesh size is equal to the number of control points in that dimension minus the spline order. Default = 4 control points in each dimension for a mesh size of 1 in each dimension.
       */
      std::vector<uint32_t> GetNumberOfControlPoints() const { return this->m_NumberOfControlPoints; }\

      /**
       * Set the spline order defining the bias field estimate. Default = 3.
       */
      SITK_RETURN_SELF_TYPE_HEADER SetSplineOrder ( uint32_t SplineOrder ) { this->m_SplineOrder = SplineOrder; return *this; }

      /**
       * Get the spline order defining the bias field estimate. Default = 3.
       */
      uint32_t GetSplineOrder() const { return this->m_SplineOrder; }\

      /**
       * Use a mask label for identifying mask functionality. See SetMaskLabel. Defaults to true.
       */
      SITK_RETURN_SELF_TYPE_HEADER SetUseMaskLabel ( bool UseMaskLabel ) { this->m_UseMaskLabel = UseMaskLabel; return *this; }

      /** Set the value of UseMaskLabel to true or false respectfully. */
      SITK_RETURN_SELF_TYPE_HEADER UseMaskLabelOn() { return this->SetUseMaskLabel(true); }
      SITK_RETURN_SELF_TYPE_HEADER UseMaskLabelOff() { return this->SetUseMaskLabel(false); }

      /**
       * Use a mask label for identifying mask functionality. See SetMaskLabel. Defaults to true.
       */
      bool GetUseMaskLabel() const { return this->m_UseMaskLabel; }\

      /**
       * Set/Get mask label value. If a binary mask image is specified and if UseMaskValue is true, only those input image voxels corresponding with mask image values equal to MaskLabel are used in estimating the bias field. If a MaskImage is specified and UseMaskLabel is false, all input image voxels corresponding to non-zero voxels in the MaskImage are used in estimating the bias field. Default = 1.
       */
      SITK_RETURN_SELF_TYPE_HEADER SetMaskLabel ( uint8_t MaskLabel ) { this->m_MaskLabel = MaskLabel; return *this; }

      /**
       * Set/Get mask label value. If a binary mask image is specified and if UseMaskValue is true, only those input image voxels corresponding with mask image values equal to MaskLabel are used in estimating the bias field. If a MaskImage is specified and UseMaskLabel is false, all input image voxels corresponding to non-zero voxels in the MaskImage are used in estimating the bias field. Default = 1.
       */
      uint8_t GetMaskLabel() const { return this->m_MaskLabel; }
     /**
      * Get the current fitting level. This is a helper function for reporting observations.
      *
      * This is an active measurement. It may be accessed while the
      * filter is being executing in command call-backs and can be
      * accessed after execution.
      */
     uint32_t GetCurrentLevel() const { return this->m_pfGetCurrentLevel(); };

     /**
      * Get the number of elapsed iterations. This is a helper function for reporting observations.
      *
      * This is an active measurement. It may be accessed while the
      * filter is being executing in command call-backs and can be
      * accessed after execution.
      */
     uint32_t GetElapsedIterations() const { return this->m_pfGetElapsedIterations(); };

     /**
      * Get the current convergence measurement. This is a helper function for reporting observations.
      *
      * This is an active measurement. It may be accessed while the
      * filter is being executing in command call-backs and can be
      * accessed after execution.
      */
     double GetCurrentConvergenceMeasurement() const { return this->m_pfGetCurrentConvergenceMeasurement(); };

     /** \brief The computed log bias field correction.
      * Typically, a reduced size image is used as input to the N4 filter using something like itkShrinkImageFilter. Since the output is a corrected version of the input, the user will probably want to apply the bias field correction to the full resolution image. Returns the b-spline log bias field reconstructioned onto the space of the referenceImage parameter.
   An input image can be corrected by: input/exp(bias_field). 
      *
      * This is an active measurement. It may be accessed while the
      * filter is being executing in command call-backs and can be
      * accessed after execution.
      */
     Image GetLogBiasFieldAsImage(Image referenceImage) const { return this->m_pfGetLogBiasFieldAsImage(referenceImage); };


      /** Name of this class */
      std::string GetName() const { return std::string ("N4BiasFieldCorrectionImageFilter"); }

      /** Print ourselves out */
      std::string ToString() const;


      /** Execute the filter on the input image */

      Image Execute ( const Image & image, const Image & maskImage );
      Image Execute ( const Image & image );

    private:

      /** Setup for member function dispatching */

      using MemberFunctionType = Image (Self::*)( const Image * image, const Image * maskImage );
      template <class TImageType> Image ExecuteInternal ( const Image * image, const Image * maskImage );


      friend struct detail::MemberFunctionAddressor<MemberFunctionType>;

      std::unique_ptr<detail::MemberFunctionFactory<MemberFunctionType> > m_MemberFactory;


      /*  */
      double  m_ConvergenceThreshold{0.001};

      /* 	odo this variable needs to be printed */
      std::vector<uint32_t>  m_MaximumNumberOfIterations{std::vector<uint32_t>(4,50)};

      /*  */
      double  m_BiasFieldFullWidthAtHalfMaximum{0.15};

      /*  */
      double  m_WienerFilterNoise{0.01};

      /*  */
      uint32_t  m_NumberOfHistogramBins{200u};

      /*  */
      std::vector<uint32_t>  m_NumberOfControlPoints{std::vector<uint32_t>(3, 4)};

      /*  */
      uint32_t  m_SplineOrder{3u};

      bool  m_UseMaskLabel{true};

      uint8_t  m_MaskLabel{1};

      /* Some global documentation */
      std::function<uint32_t()> m_pfGetCurrentLevel;
      /* Some global documentation */
      std::function<uint32_t()> m_pfGetElapsedIterations;
      /* Some global documentation */
      std::function<double()> m_pfGetCurrentConvergenceMeasurement;
      /* Some global documentation */
      std::function<Image(Image)> m_pfGetLogBiasFieldAsImage;

      // Holder of process object for active measurements
      itk::ProcessObject *m_Filter{nullptr};

    };

    /**\
     * \brief Implementation of the N4 bias field correction algorithm.
     *
     * This function directly calls the execute method of N4BiasFieldCorrectionImageFilter
     * in order to support a procedural API
     *
     * \sa itk::simple::N4BiasFieldCorrectionImageFilter for the object oriented interface
     * @{
     */

     SITKBasicFilters_EXPORT Image N4BiasFieldCorrection ( const Image & image, const Image & maskImage, double convergenceThreshold = 0.001, std::vector<uint32_t> maximumNumberOfIterations = std::vector<uint32_t>(4,50), double biasFieldFullWidthAtHalfMaximum = 0.15, double wienerFilterNoise = 0.01, uint32_t numberOfHistogramBins = 200u, std::vector<uint32_t> numberOfControlPoints = std::vector<uint32_t>(3, 4), uint32_t splineOrder = 3u, bool useMaskLabel = true, uint8_t maskLabel = 1 );
     SITKBasicFilters_EXPORT Image N4BiasFieldCorrection ( const Image & image, double convergenceThreshold = 0.001, std::vector<uint32_t> maximumNumberOfIterations = std::vector<uint32_t>(4,50), double biasFieldFullWidthAtHalfMaximum = 0.15, double wienerFilterNoise = 0.01, uint32_t numberOfHistogramBins = 200u, std::vector<uint32_t> numberOfControlPoints = std::vector<uint32_t>(3, 4), uint32_t splineOrder = 3u, bool useMaskLabel = true, uint8_t maskLabel = 1 );
     /** @} */
}
#endif
