/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  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 itkRGBGibbsPriorFilter_hxx
#define itkRGBGibbsPriorFilter_hxx

#include "itkRGBGibbsPriorFilter.h"
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <cmath>

#define RGBGibbsPriorFilterNeedsDebugging 1

namespace itk
{
/* Set initial value of some parameters in the constructor */
template< typename TInputImage, typename TClassifiedImage >
RGBGibbsPriorFilter< TInputImage, TClassifiedImage >
::RGBGibbsPriorFilter():
  m_InputImage(nullptr),
  m_TrainingImage(nullptr),
  m_LabelledImage(nullptr),
  m_ClassifierPtr(nullptr),
  m_MediumImage(nullptr),
  m_LowPoint()

{
  m_StartPoint.Fill(0);
}

template< typename TInputImage, typename TClassifiedImage >
RGBGibbsPriorFilter< TInputImage, TClassifiedImage >
::~RGBGibbsPriorFilter()
{
  delete[] m_Region;
  delete[] m_RegionCount;
  delete[] m_LabelStatus;
}

/* Set the labelled image. */
template< typename TInputImage, typename TClassifiedImage >
void
RGBGibbsPriorFilter< TInputImage, TClassifiedImage >
::SetLabelledImage(LabelledImageType image)
{
  m_LabelledImage = image;
  this->Allocate();
}

/* GenerateMediumImage method. */
template< typename TInputImage, typename TClassifiedImage >
void
RGBGibbsPriorFilter< TInputImage, TClassifiedImage >
::GenerateMediumImage()
{
  InputImageConstPointer input = this->GetInput();

  m_MediumImage = TInputImage::New();
  m_MediumImage->SetLargestPossibleRegion( input->GetLargestPossibleRegion() );
  m_MediumImage->SetRequestedRegionToLargestPossibleRegion();
  m_MediumImage->SetBufferedRegion( m_MediumImage->GetRequestedRegion() );
  m_MediumImage->Allocate();
}

/* Allocate the memeory for classified image. */
template< typename TInputImage, typename TClassifiedImage >
void
RGBGibbsPriorFilter< TInputImage, TClassifiedImage >
::Allocate()
{
  /* Get the image width/height and depth. */
  InputImageSizeType inputImageSize = m_InputImage->GetBufferedRegion().GetSize();

  m_ImageWidth  = inputImageSize[0];
  m_ImageHeight = inputImageSize[1];
  m_ImageDepth  = inputImageSize[2];

  const unsigned int numberOfPixels = m_ImageWidth * m_ImageHeight * m_ImageDepth;
  m_LabelStatus = new LabelType[numberOfPixels];
  for ( unsigned int index = 0; index < numberOfPixels; index++ )
    {
    m_LabelStatus[index] = 1;
    }
}

/* Smooth the image in piecewise fashion. */
template< typename TInputImage, typename TClassifiedImage >
void
RGBGibbsPriorFilter< TInputImage, TClassifiedImage >
::GreyScalarBoundary(LabelledImageIndexType Index3D)
{
  int       change;
  int       signs[4];
  int       x;
  int       numx;
  LabelType origin;
  LabelType neighbors[4];

  neighbors[0] = neighbors[1] = neighbors[2] = neighbors[3] = 0;

  for ( unsigned int rgb = 0; rgb < m_VecDim; rgb++ )
    {
    origin = static_cast< LabelType >( m_InputImage->GetPixel(Index3D)[rgb] );
    unsigned int j = 0;

    for ( unsigned int i = 0; i < ImageDimension - 1; i++ )
      {
      Index3D[i]--;
      neighbors[j] = static_cast< LabelType >( m_InputImage->GetPixel(Index3D)[rgb] );
      Index3D[i]++;
      j++;

      Index3D[i]++;
      neighbors[j] = static_cast< LabelType >( m_InputImage->GetPixel(Index3D)[rgb] );
      Index3D[i]--;
      j++;
      }

    for (auto & sign : signs)
      {
      sign = 0;
      }

    /* Compute the minimum points of piecewise smoothness */
    m_LowPoint[rgb] = origin;
    change = 1;
    x = origin;
    numx = 1;

    while ( change > 0 )
      {
      change = 0;

      for ( unsigned int i = 0; i < 4; i++ )
        {
        if ( signs[i] == 0 )
          {
          const auto difference = static_cast< LabelType >( itk::Math::abs(m_LowPoint[rgb] - neighbors[i]) );
          if ( difference < m_BoundaryGradient )
            {
            numx++;
            x += neighbors[i];
            signs[i]++;
            change++;
            }
          }
        }

      m_LowPoint[rgb] = x / numx;

      for ( unsigned int i = 0; i < 4; i++ )
        {
        if ( signs[i] == 1 )
          {
          const auto difference = static_cast< LabelType >( itk::Math::abs(m_LowPoint[rgb] - neighbors[i]) );
          if ( difference > m_BoundaryGradient )
            {
            numx--;
            x -= neighbors[i];
            signs[i]--;
            change++;
            }
          }
        }

      m_LowPoint[rgb] = x / numx;
      }
    }
}

/* Set the classifier. */
template< typename TInputImage, typename TClassifiedImage >
void
RGBGibbsPriorFilter< TInputImage, TClassifiedImage >
::SetClassifier(typename ClassifierType::Pointer ptrToClassifier)
{
  m_ClassifierPtr = ptrToClassifier;
  m_ClassifierPtr->SetNumberOfClasses(m_NumberOfClasses);
}

/* Set the training image. */
template< typename TInputImage, typename TClassifiedImage >
void
RGBGibbsPriorFilter< TInputImage, TClassifiedImage >
::SetTrainingImage(TrainingImageType image)
{
  m_TrainingImage = image;
}

/* Check if 2 number are identical. */
template< typename TInputImage, typename TClassifiedImage >
int
RGBGibbsPriorFilter< TInputImage, TClassifiedImage >
::Sim(int a, int b)
{
  if ( a == b ) { return 1; }
  return 0;
}

/* GibbsTotalEnergy method that minimizes the local characteristic(f_2) term
 * in the energy function. */
template< typename TInputImage, typename TClassifiedImage >
void
RGBGibbsPriorFilter< TInputImage, TClassifiedImage >
::GibbsTotalEnergy(int i)
{
  LabelledImageIndexType offsetIndex3D;

  offsetIndex3D.Fill(0);

  int size = m_ImageWidth * m_ImageHeight * m_ImageDepth;
  int frame = m_ImageWidth * m_ImageHeight;
  int rowsize = m_ImageWidth;

  double       energy[2];
  double       difenergy;
  LabelType    label;
  LabelType    originlabel;
  LabelType    f[8];
  unsigned int j;
  unsigned int k;
  unsigned int neighborcount = 0;

  offsetIndex3D[2] = i / frame;
  offsetIndex3D[1] = ( i % frame ) / rowsize;
  offsetIndex3D[0] = ( i % frame ) % rowsize;

  if ( ( i > rowsize - 1 ) && ( ( i % rowsize ) != rowsize - 1 )
       && ( i < size - rowsize ) && ( ( i % rowsize ) != 0 ) ) \
    {
    offsetIndex3D[0]--;
    offsetIndex3D[1]--;
    f[neighborcount++] = (int)m_LabelledImage->GetPixel(offsetIndex3D);

    offsetIndex3D[0]++;
    f[neighborcount++] = (int)m_LabelledImage->GetPixel(offsetIndex3D);

    offsetIndex3D[0]++;
    f[neighborcount++] = (int)m_LabelledImage->GetPixel(offsetIndex3D);

    offsetIndex3D[1]++;
    f[neighborcount++] = (int)m_LabelledImage->GetPixel(offsetIndex3D);

    offsetIndex3D[1]++;
    f[neighborcount++] = (int)m_LabelledImage->GetPixel(offsetIndex3D);

    offsetIndex3D[0]--;
    f[neighborcount++] = (int)m_LabelledImage->GetPixel(offsetIndex3D);

    offsetIndex3D[0]--;
    f[neighborcount++] = (int)m_LabelledImage->GetPixel(offsetIndex3D);

    offsetIndex3D[1]--;
    f[neighborcount] = (int)m_LabelledImage->GetPixel(offsetIndex3D);
    }

  k = 0;
  for ( j = 0; j < 8; j++ )
    {
    if ( f[j] == m_ObjectLabel )
      {
      k++;
      }
    }

  bool changeflag = ( k > 3 );

  for ( unsigned int jj = 0; jj < 2; jj++ )
    {
    energy[jj] = 0;
    energy[jj] += GibbsEnergy(i,           0, jj);
    energy[jj] += GibbsEnergy(i + rowsize + 1, 1, jj);
    energy[jj] += GibbsEnergy(i + rowsize,   2, jj);
    energy[jj] += GibbsEnergy(i + rowsize - 1, 3, jj);
    energy[jj] += GibbsEnergy(i - 1,         4, jj);
    energy[jj] += GibbsEnergy(i - rowsize - 1, 5, jj);
    energy[jj] += GibbsEnergy(i - rowsize,   6, jj);
    energy[jj] += GibbsEnergy(i - rowsize + 1, 7, jj);
    energy[jj] += GibbsEnergy(i + 1,         8, jj);
    if ( m_LabelStatus[i] == jj )
      {
      energy[jj] += -3;
      }
    else
      {
      energy[jj] += 3;
      }
    }

  if ( energy[0] < energy[1] )
    {
    label = 0;
    }
  else
    {
    label = 1;
    }

  originlabel = m_LabelledImage->GetPixel(offsetIndex3D);
  if ( originlabel != label )
    {
    m_LabelledImage->SetPixel(offsetIndex3D, label);
    }

  else
    {
    if ( changeflag )
      {
      difenergy = energy[label] - energy[1 - label];
      auto rand_num = (double)( rand() / 32768.0 );
      auto energy_num = (double)( std::exp( (double)( difenergy * 0.5 * size / ( 2 * size - m_Temp ) ) ) );
      if ( rand_num < energy_num )
        {
        m_LabelledImage->SetPixel(offsetIndex3D, 1 - label);
        }
      }
    }
}

template< typename TInputImage, typename TClassifiedImage >
double
RGBGibbsPriorFilter< TInputImage, TClassifiedImage >
::GibbsEnergy(unsigned int i, unsigned int k, unsigned int k1)
{
  LabelledImageRegionIterator
  labelledImageIt( m_LabelledImage, m_LabelledImage->GetBufferedRegion() );

  LabelType    f[8];
  int          j;
  unsigned int neighborcount = 0;
  int          simnum    = 0;
  int          changenum = 0;
  bool         changeflag;
  double       res = 0.0;

  LabelledImageIndexType offsetIndex3D;

  offsetIndex3D.Fill(0);

  LabelledImagePixelType labelledPixel = 0;

  const unsigned int size     = m_ImageWidth * m_ImageHeight * m_ImageDepth;
  const unsigned int frame    = m_ImageWidth * m_ImageHeight;
  const unsigned int rowsize  = m_ImageWidth;

  offsetIndex3D[1] = ( i % frame ) / rowsize;
  offsetIndex3D[0] = ( i % frame ) % rowsize;

  if ( k != 0 )
    {
    labelledPixel =
      (LabelledImagePixelType)m_LabelledImage->GetPixel(offsetIndex3D);
    }

  if ( (      i       > rowsize - 1    )
       && ( ( i % rowsize ) != rowsize - 1 )
       && (      i       < size - rowsize )
       && ( ( i % rowsize ) != 0 ) )
    {
    offsetIndex3D[0]--;
    offsetIndex3D[1]--;
    f[neighborcount++] = static_cast< LabelType >( m_LabelledImage->GetPixel(offsetIndex3D) );

    offsetIndex3D[0]++;
    f[neighborcount++] = static_cast< LabelType >( m_LabelledImage->GetPixel(offsetIndex3D) );

    offsetIndex3D[0]++;
    f[neighborcount++] = static_cast< LabelType >( m_LabelledImage->GetPixel(offsetIndex3D) );

    offsetIndex3D[1]++;
    f[neighborcount++] = static_cast< LabelType >( m_LabelledImage->GetPixel(offsetIndex3D) );

    offsetIndex3D[1]++;
    f[neighborcount++] = static_cast< LabelType >( m_LabelledImage->GetPixel(offsetIndex3D) );

    offsetIndex3D[0]--;
    f[neighborcount++] = static_cast< LabelType >( m_LabelledImage->GetPixel(offsetIndex3D) );

    offsetIndex3D[0]--;
    f[neighborcount++] = static_cast< LabelType >( m_LabelledImage->GetPixel(offsetIndex3D) );

    offsetIndex3D[1]--;
    f[neighborcount++] = static_cast< LabelType >( m_LabelledImage->GetPixel(offsetIndex3D) );
    }

  /* Pixels at the edge of image will be dropped. */
  if ( neighborcount != 8 )
    {
    return 0.0;
    }

  if ( k != 0 )
    {
    f[k - 1] = k1;
    }
  else
    {
    labelledPixel = k1;
    }

  changeflag = ( f[0] == labelledPixel );

  /* Assuming we are segmenting objects with smooth boundaries, we give
    * weight to local characteristics */
  for ( j = 0; j < 8; j++ )
    {
    if ( ( f[j] == labelledPixel ) != changeflag )
      {
      changenum++;
      changeflag = !changeflag;
      }

    if ( changeflag )
      {
      if ( j % 2 == 0 )
        {
        res -= 0.7;
        }
      else
        {
        res -= 1.0;
        }
      simnum++;
      }
    }

  if ( changenum < 3 )
    {
    if ( ( simnum == 4 ) || ( simnum == 5 ) )
      {
      return res -= m_CliqueWeight_2;
      }
    }

  if ( simnum == 8 )
    {
    return res -= m_CliqueWeight_4;
    }
  else
    {
    return res -= m_CliqueWeight_6;
    }
}

template< typename TInputImage, typename TClassifiedImage >
void
RGBGibbsPriorFilter< TInputImage, TClassifiedImage >
::GenerateData()
{
  /* First run the Gaussian classifier calculator and
   *  generate the Gaussian model for the different classes.
   *  Then generate the initial labelled dataset.*/

  m_InputImage = this->GetInput();
  m_VecDim     = InputPixelType::GetVectorDimension();

  GenerateMediumImage();

  /* Pass the input image and training image set to the
   *  classifier. For the first iteration, use the original image.
   *  In the following loops, you can use the result provided by a segmentation
   *  method such as the deformable model. */
  m_ClassifierPtr->SetInputImage(m_InputImage);
  /* Create the training image using the original image or the output
   *  of a segmentation method such as the deformable model. */
//  m_ClassifierPtr->SetTrainingImage( m_TrainingImage );

  /* Run the Gaussian classifier algorithm. */
  m_ClassifierPtr->Update();

  SetLabelledImage( m_ClassifierPtr->GetClassifiedImage() );

  ApplyGPImageFilter();
  /* Set the output labelled image and allocate the memory. */
  LabelledImageType outputPtr = this->GetOutput();

  if ( m_RecursiveNumber == 0 )
    {
    outputPtr->SetLargestPossibleRegion( m_InputImage->GetLargestPossibleRegion() );
    outputPtr->SetBufferedRegion( m_InputImage->GetLargestPossibleRegion() );
    }

  /* Allocate the output buffer memory. */
  outputPtr->Allocate();

  /* Copy labelled result to the Output buffer and set the iterators of
    * the processed image.   */
  LabelledImageRegionIterator
  labelledImageIt( m_LabelledImage, m_LabelledImage->GetBufferedRegion() );

  /* Set the iterators of the output image buffer. */
  LabelledImageRegionIterator
  outImageIt( outputPtr, outputPtr->GetBufferedRegion() );

  while ( !outImageIt.IsAtEnd() )
    {
    auto labelvalue = (LabelledImagePixelType)labelledImageIt.Get();
    outImageIt.Set(labelvalue);
    ++labelledImageIt;
    ++outImageIt;
    }

  m_RecursiveNumber++;
}

template< typename TInputImage, typename TClassifiedImage >
void
RGBGibbsPriorFilter< TInputImage, TClassifiedImage >
::ApplyGPImageFilter()
{
  /* Minimize f_1 and f_3. */
  MinimizeFunctional();
}

template< typename TInputImage, typename TClassifiedImage >
void
RGBGibbsPriorFilter< TInputImage, TClassifiedImage >
::MinimizeFunctional()
{
  /* This implementation uses the SA algorithm. */

  ApplyGibbsLabeller();

  RegionEraser();

#ifndef RGBGibbsPriorFilterNeedsDebugging
  const unsigned int size = m_ImageWidth * m_ImageHeight * m_ImageDepth;
  const unsigned int rowsize = m_ImageWidth;

  m_Temp = 0;
  srand( static_cast< unsigned int >( time(nullptr) ) );

  while ( m_Temp < 2 * size )
    {
    unsigned int randomPixel = static_cast< unsigned int >( size * rand() / RAND_MAX );
    if ( ( randomPixel > ( rowsize - 1 ) ) && ( randomPixel < ( size - rowsize ) )
         && ( randomPixel % rowsize != 0 ) && ( randomPixel % rowsize != rowsize - 1 ) )
      {
      GibbsTotalEnergy(randomPixel); // minimized f_2;
      }
    m_Temp++;
    }
#endif
}

template< typename TInputImage, typename TClassifiedImage >
void
RGBGibbsPriorFilter< TInputImage, TClassifiedImage >
::ApplyGibbsLabeller()
{
  /* Set the iterators and the pixel type definition for the input image. */
  InputImageRegionConstIterator inputImageIt( m_InputImage,
                                              m_InputImage->GetBufferedRegion() );

  InputImageRegionIterator mediumImageIt( m_MediumImage,
                                          m_MediumImage->GetBufferedRegion() );

  /* Set the iterators and the pixel type definition for the classified image.
    */
  LabelledImageRegionIterator
  labelledImageIt( m_LabelledImage, m_LabelledImage->GetBufferedRegion() );

  /* Variable to store the origin pixel vector value. */
  InputImagePixelType originPixelVec;

  /* Variable to store the modified pixel vector value. */
  InputImagePixelType changedPixelVec;
  changedPixelVec.Fill( NumericTraits< typename InputImagePixelType::ValueType >::ZeroValue() );

  /* Set a variable to store the offset index. */
  LabelledImageIndexType offsetIndex3D; offsetIndex3D.Fill(0);

  auto * dist = new double[m_NumberOfClasses];

  const unsigned int size    = m_ImageWidth * m_ImageHeight * m_ImageDepth;
  const unsigned int frame   = m_ImageWidth * m_ImageHeight;
  const unsigned int rowsize = m_ImageWidth;

  inputImageIt.GoToBegin();
  mediumImageIt.GoToBegin();
  labelledImageIt.GoToBegin();

  unsigned int i = 0;
  while ( !inputImageIt.IsAtEnd() )
    {
    offsetIndex3D[2] =  i / frame;
    offsetIndex3D[1] = ( i % frame ) / rowsize;
    offsetIndex3D[0] = ( i % frame ) % rowsize;

    if ( ( i > ( rowsize - 1 ) ) && ( i < ( size - rowsize ) )
         && ( i % rowsize != 0 ) && ( i % rowsize != rowsize - 1 ) )
      {
      originPixelVec = inputImageIt.Get();
      GreyScalarBoundary(offsetIndex3D);
      for ( unsigned int rgb = 0; rgb < m_VecDim; rgb++ )
        {
        changedPixelVec[rgb] = m_LowPoint[rgb];
        }
      /* mediumImageIt.Set(changedPixelVec); */
      }
    else
      {
      changedPixelVec = inputImageIt.Get();
      }

    const std::vector< double > & distValue =
      m_ClassifierPtr->GetPixelMembershipValue(changedPixelVec);

    LabelType pixLabel;
    if ( distValue[1] > m_ObjectThreshold )
      {
      pixLabel = 0;
      }
    else
      {
      pixLabel = 1;
      }
    labelledImageIt.Set(pixLabel);

    i++;
    ++labelledImageIt;
    ++inputImageIt;
    ++mediumImageIt;
    }

  delete[] dist;
}

/* Remove the tiny bias inside the object region. */
template< typename TInputImage, typename TClassifiedImage >
void
RGBGibbsPriorFilter< TInputImage, TClassifiedImage >
::RegionEraser()
{
  const unsigned int size = m_ImageWidth * m_ImageHeight * m_ImageDepth;

  delete[] m_Region;
  m_Region = new unsigned short[size];

  delete[] m_RegionCount;
  m_RegionCount = new unsigned short[size];

  auto * valid_region_counter = new unsigned short[size];

  LabelledImageRegionIterator
  labelledImageIt( m_LabelledImage, m_LabelledImage->GetBufferedRegion() );

  for ( unsigned int r = 0; r < size; r++ )
    {
    m_Region[r] = 0;
    m_RegionCount[r] = 1;
    valid_region_counter[r] = 0;
    }

  LabelType i = NumericTraits< LabelType >::ZeroValue();
  LabelType k = NumericTraits< LabelType >::ZeroValue();
  LabelType l = NumericTraits< LabelType >::ZeroValue();
  LabelType label;

  while ( !labelledImageIt.IsAtEnd() )
    {
    if ( m_Region[i] == 0 )
      {
      label = labelledImageIt.Get();
      if ( LabelRegion(i, ++l, label) > m_ClusterSize )
        {
        valid_region_counter[k] = l;
        k++;
        }
      }

    i++;
    ++labelledImageIt;
    }

  i = 0;
  unsigned int j;
  labelledImageIt.GoToBegin();

  while ( !labelledImageIt.IsAtEnd() )
    {
    j = 0;
    while ( ( m_Region[i] != valid_region_counter[j] ) && ( j < k ) )
      {
      j++;
      }

    if ( j == k )
      {
      label = labelledImageIt.Get();
      labelledImageIt.Set(1 - label);
      }
    i++;
    ++labelledImageIt;
    }

  delete[] valid_region_counter;
}

template< typename TInputImage, typename TClassifiedImage >
unsigned int
RGBGibbsPriorFilter< TInputImage, TClassifiedImage >
::LabelRegion(int i, int l, int change)
{
  unsigned int       count = 1;
  int                m;
  const unsigned int frame   = m_ImageWidth * m_ImageHeight;
  const unsigned int rowsize = m_ImageWidth;

  LabelledImageIndexType offsetIndex3D;

  offsetIndex3D.Fill(0);

  m_Region[i] = l;

  offsetIndex3D[2] = i / frame;
  offsetIndex3D[1] = ( i % frame ) / rowsize;
  offsetIndex3D[0] = ( i % frame ) % rowsize;

  if ( offsetIndex3D[0] > 0 )
    {
    offsetIndex3D[0]--;
    m = m_LabelledImage->GetPixel(offsetIndex3D);
    if ( ( m == change ) && ( m_Region[i - 1] == 0 ) )
      {
      count += LabelRegion(i - 1, l, change);
      }
    offsetIndex3D[0]++;
    }

  if ( offsetIndex3D[0] < static_cast< IndexValueType >( m_ImageWidth - 1 ) )
    {
    offsetIndex3D[0]++;
    m = m_LabelledImage->GetPixel(offsetIndex3D);
    if ( ( m == change ) && ( m_Region[i + 1] == 0 ) )
      {
      count += LabelRegion(i + 1, l, change);
      }
    offsetIndex3D[0]--;
    }

  if ( offsetIndex3D[1] > 0 )
    {
    offsetIndex3D[1]--;
    m = m_LabelledImage->GetPixel(offsetIndex3D);
    if ( ( m == change ) && ( m_Region[i - rowsize] == 0 ) )
      {
      count += LabelRegion(i - rowsize, l, change);
      }
    offsetIndex3D[1]++;
    }

  if ( offsetIndex3D[1] < static_cast< IndexValueType >( m_ImageHeight - 1 ) )
    {
    offsetIndex3D[1]++;
    m = m_LabelledImage->GetPixel(offsetIndex3D);
    if ( ( m == change ) && ( m_Region[i + rowsize] == 0 ) )
      {
      count += LabelRegion(i + rowsize, l, change);
      }
    offsetIndex3D[1]--;
    }

  return count;
}

template< typename TInputImage, typename TClassifiedImage >
void
RGBGibbsPriorFilter< TInputImage, TClassifiedImage >
::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);

  os << indent << "NumberOfClasses: "
     << m_NumberOfClasses << std::endl;
  os << indent << "MaximumNumberOfIterations: "
     << m_MaximumNumberOfIterations << std::endl;
  os << indent << "ObjectThreshold: "
     << m_ObjectThreshold << std::endl;
  os << indent << "BoundaryGradient: "
     << m_BoundaryGradient << std::endl;
  os << indent << "CliqueWeight_1: "
     << m_CliqueWeight_1 << std::endl;
  os << indent << "CliqueWeight_2: "
     << m_CliqueWeight_2 << std::endl;
  os << indent << "CliqueWeight_3: "
     << m_CliqueWeight_3 << std::endl;
  os << indent << "CliqueWeight_4: "
     << m_CliqueWeight_4 << std::endl;
  os << indent << "CliqueWeight_5: "
     << m_CliqueWeight_5 << std::endl;
  os << indent << "CliqueWeight_6: "
     << m_CliqueWeight_6 << std::endl;
  os << indent << "ClusterSize: "
     << m_ClusterSize << std::endl;
  os << indent << "ObjectLabel: "
     << m_ObjectLabel << std::endl;
  os << indent << "StartPoint: "
     << m_StartPoint << std::endl;
  if ( m_TrainingImage )
    {
    os << "TraingImage: " << m_TrainingImage;
    }
  if ( m_LabelledImage )
    {
    os << "TrainingImage: " << m_TrainingImage;
    }
  if ( m_ClassifierPtr )
    {
    os << "ClassifierPtr: " << m_ClassifierPtr;
    }
}
} /* end namespace itk. */

#endif
