/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2025 Univ. Grenoble Alpes, CNRS, Grenoble INP - UGA, TIMC, 38000 Grenoble, France
 *
 * Visit http://camitk.imag.fr for more information
 *
 * This file is part of CamiTK.
 *
 * CamiTK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * CamiTK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with CamiTK.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $CAMITK_LICENCE_END$
 ****************************************************************************/
// CamiTK includes
#include "MorphologicalOperators.h"
#include <ItkProgressObserver.h>
#include <Property.h>

// Itk includes
#include <itkImageToVTKImageFilter.h>
#include <itkVTKImageToImageFilter.h>
#include <itkBinaryBallStructuringElement.h>
#include <itkBinaryErodeImageFilter.h>
#include <itkBinaryDilateImageFilter.h>
#include <itkGrayscaleErodeImageFilter.h>
#include <itkGrayscaleDilateImageFilter.h>


using namespace camitk;

// --------------- constructor -------------------
MorphologicalOperators::MorphologicalOperators(ActionExtension* extension) : Action(extension) {
    // Setting name, description and input component
    setName("Morphological Operators");
    setDescription("<br/><b>Mathematical morphology</b> has proved to be a powerful resource for image processing and analysis.<br/><br/>This filter implements classical mathematical morphology operators:\
<ul><li>erosion</li><li>dilation</li><li>opening</li><li>closure</li></ul>");

    setComponentClassName("ImageComponent");

    // Setting classification family and tags
    this->setFamily("ITK Filter");
    this->addTag("Morphology");
    this->addTag("Operations");
    this->addTag("Neighborhood Filter");

    // Setting parameters default values
    Property* structuringElementSizeProperty = new Property(tr("Structuring element size"), 1, tr("The structuring element size 'n'' represents the dimension of the n x n x n voxels square used by the operator.\nFor instance a 3 voxels size will indicate a 3x3x3 square."), "");
    structuringElementSizeProperty->setAttribute("minimum", 1);
    structuringElementSizeProperty->setAttribute("maximum", 100);
    structuringElementSizeProperty->setAttribute("singleStep", 1);
    addParameter(structuringElementSizeProperty);

    Property* morphoTypeProperty = new Property(tr("Image scalar type"), GREY_LEVEL, tr("The image scalar type."), "");
    morphoTypeProperty->setEnumTypeName("MorphoType", this);
    addParameter(morphoTypeProperty);

    Property* morphoOperationProperty = new Property(tr("Type of operation"), EROSION, tr("Select the desired morphological operation."), "");
    morphoOperationProperty->setEnumTypeName("MorphoOperation", this);
    addParameter(morphoOperationProperty);
}

// --------------- destructor -------------------
MorphologicalOperators::~MorphologicalOperators() {
    // do not delete the widget has it might have been used in the ActionViewer (i.e. the ownership might have been taken by the stacked widget)
}

MorphologicalOperators::MorphoType MorphologicalOperators::getMorphoType() const {
    return (MorphoType) getParameterValue("Image scalar type").toInt();
}

MorphologicalOperators::MorphoOperation MorphologicalOperators::getMorphoOperation() const {
    return (MorphoOperation) getParameterValue("Type of operation").toInt();
}

// --------------- apply -------------------
Action::ApplyStatus MorphologicalOperators::apply() {
    for (Component* comp : getTargets()) {
        ImageComponent* input = dynamic_cast<ImageComponent*>(comp);
        process(input);
    }
    return SUCCESS;
}

void MorphologicalOperators::process(ImageComponent* comp) {
    // Get the parameters
    this->structuringElementSize = getParameterValue("Structuring element size").toInt();

    // ITK filter implementation using templates
    vtkSmartPointer<vtkImageData> inputImage = comp->getImageData();
    vtkSmartPointer<vtkImageData> outputImage = implementProcess(inputImage);

    ImageComponent* outputComp = new ImageComponent(outputImage, comp->getName() + suffix);

    // same frame as input
    outputComp->setFrameFrom(comp);

    refreshApplication();

}

#include "MorphologicalOperators.impl"

// ITK filter implementation
template <class InputPixelType, class OutputPixelType, const int dim>
vtkSmartPointer<vtkImageData> MorphologicalOperators::itkProcess(vtkSmartPointer<vtkImageData> img) {
    vtkSmartPointer<vtkImageData> result = nullptr;

    switch (this->getMorphoType()) {

        case MorphologicalOperators::GREY_LEVEL:

            switch (this->getMorphoOperation()) {
                case MorphologicalOperators::EROSION:
                    suffix = "_eroded";
                    result = greyLevelErosionFilter<InputPixelType, OutputPixelType, dim>(img);
                    break;
                case MorphologicalOperators::DILATION:
                    suffix = "_dilated";
                    result = greyLevelDilationFilter<InputPixelType, OutputPixelType, dim>(img);
                    break;
                case MorphologicalOperators::OPENING:
                    suffix = "_opened";
                    result = greyLevelOpeningFilter<InputPixelType, OutputPixelType, dim>(img);
                    break;
                case MorphologicalOperators::CLOSING:
                    suffix = "_closed";
                    result = greyLevelClosureFilter<InputPixelType, OutputPixelType, dim>(img);
                    break;
                default:
                    break;
            }

            break;
        case MorphologicalOperators::BINARY:

            switch (this->getMorphoOperation()) {
                case MorphologicalOperators::EROSION:
                    suffix = "_eroded";
                    result = binaryErosionFilter<InputPixelType, OutputPixelType, dim>(img);
                    break;
                case MorphologicalOperators::DILATION:
                    suffix = "_dilated";
                    result = binaryDilationFilter<InputPixelType, OutputPixelType, dim>(img);
                    break;
                case MorphologicalOperators::OPENING:
                    suffix = "_opened";
                    result = binaryOpeningFilter<InputPixelType, OutputPixelType, dim>(img);
                    break;
                case MorphologicalOperators::CLOSING:
                    suffix = "_closed";
                    result = binaryClosureFilter<InputPixelType, OutputPixelType, dim>(img);
                    break;
                default:
                    break;
            }

            break;
        default:
            break;
    }

    return result;

}

template <class InputPixelType, class OutputPixelType, const int dim>
vtkSmartPointer<vtkImageData> MorphologicalOperators::binaryErosionFilter(vtkSmartPointer<vtkImageData> img) {



    // --------------------- Filters declaration and creation ----------------------
    // Define ITK input and output image types with respect to the instantiation
    //    types of the tamplate.
    typedef itk::Image< InputPixelType,  dim > InputImageType;
    typedef itk::Image< OutputPixelType, dim > OutputImageType;

    // Convert the image from CamiTK in VTK format to ITK format to use ITK filters.
    typedef itk::VTKImageToImageFilter<InputImageType> vtkToItkFilterType;
    typename vtkToItkFilterType::Pointer vtkToItkFilter = vtkToItkFilterType::New();

    // Declare and create your own private ITK filter here...
    typedef itk::BinaryBallStructuringElement<InputPixelType, dim> StructuringElementType;
    typedef itk::BinaryErodeImageFilter <InputImageType, OutputImageType, StructuringElementType> FilterType;
    typename FilterType::Pointer filter = FilterType::New();

    // In the same way, once the image is filtered, we need to convert it again to
    //    VTK format to give it to CamiTK.
    typedef itk::ImageToVTKImageFilter<OutputImageType> itkToVtkFilterType;
    typename itkToVtkFilterType::Pointer itkToVtkFilter = itkToVtkFilterType::New();

    // To update CamiTK progress bar while filtering, add an ITK observer to the filters.
    ItkProgressObserver::Pointer observer = ItkProgressObserver::New();
    // ITK observers generally give values between 0 and 1, and CamiTK progress bar
    //    wants values between 0 and 100...
    observer->SetCoef(100.0);

    // --------------------- Plug filters and parameters ---------------------------
    // From VTK to ITK
    vtkToItkFilter->SetInput(img);
    // For the filter itself
    filter->SetInput(vtkToItkFilter->GetOutput());
    filter->AddObserver(itk::ProgressEvent(), observer);

    StructuringElementType structuringElement;
    structuringElement.SetRadius(structuringElementSize);
    structuringElement.CreateStructuringElement();
    filter->SetKernel(structuringElement);

    filter->Update();

    // From ITK to VTK
    itkToVtkFilter->SetInput(filter->GetOutput());

    // --------------------- Actually execute all filters parts --------------------
    itkToVtkFilter->Update();


    // --------------------- Create and return a copy (the filters will be deleted)--
    vtkSmartPointer<vtkImageData> resultImage = itkToVtkFilter->GetOutput();
    vtkSmartPointer<vtkImageData> filteredImage = vtkSmartPointer<vtkImageData>::New();
    int extent[6];
    resultImage->GetExtent(extent);
    filteredImage->SetExtent(extent);
    filteredImage->DeepCopy(resultImage);

    observer->Reset();
    observer = nullptr;

    return filteredImage;

}

template <class InputPixelType, class OutputPixelType, const int dim>
vtkSmartPointer<vtkImageData> MorphologicalOperators::binaryDilationFilter(vtkSmartPointer<vtkImageData> img) {

    // --------------------- Filters declaration and creation ----------------------
    // Define ITK input and output image types with respect to the instantiation
    //    types of the tamplate.
    typedef itk::Image< InputPixelType,  dim > InputImageType;
    typedef itk::Image< OutputPixelType, dim > OutputImageType;

    // Convert the image from CamiTK in VTK format to ITK format to use ITK filters.
    typedef itk::VTKImageToImageFilter<InputImageType> vtkToItkFilterType;
    typename vtkToItkFilterType::Pointer vtkToItkFilter = vtkToItkFilterType::New();

    // In the same way, once the image is filtered, we need to convert it again to
    //    VTK format to give it to CamiTK.
    typedef itk::ImageToVTKImageFilter<OutputImageType> itkToVtkFilterType;
    typename itkToVtkFilterType::Pointer itkToVtkFilter = itkToVtkFilterType::New();

    // Declare and create your own private ITK filter here...
    typedef itk::BinaryBallStructuringElement<InputPixelType, dim> StructuringElementType;
    typedef itk::BinaryDilateImageFilter <InputImageType, OutputImageType, StructuringElementType> FilterType;
    typename FilterType::Pointer filter = FilterType::New();

    // To update CamiTK progress bar while filtering, add an ITK observer to the filters.
    ItkProgressObserver::Pointer observer = ItkProgressObserver::New();
    // ITK observers generally give values between 0 and 1, and CamiTK progress bar
    //    wants values between 0 and 100...
    observer->SetCoef(100.0);

    // --------------------- Plug filters and parameters ---------------------------
    // From VTK to ITK
    vtkToItkFilter->SetInput(img);
    // For the filter itself
    filter->SetInput(vtkToItkFilter->GetOutput());
    filter->AddObserver(itk::ProgressEvent(), observer);

    StructuringElementType structuringElement;
    structuringElement.SetRadius(structuringElementSize);
    structuringElement.CreateStructuringElement();
    filter->SetKernel(structuringElement);

    filter->Update();

    // From ITK to VTK
    itkToVtkFilter->SetInput(filter->GetOutput());

    // --------------------- Actually execute all filters parts --------------------
    itkToVtkFilter->Update();


    // --------------------- Create and return a copy (the filters will be deleted)--
    vtkSmartPointer<vtkImageData> resultImage = itkToVtkFilter->GetOutput();
    vtkSmartPointer<vtkImageData> filteredImage = vtkSmartPointer<vtkImageData>::New();
    int extent[6];
    resultImage->GetExtent(extent);
    filteredImage->SetExtent(extent);
    filteredImage->DeepCopy(resultImage);

    observer->Reset();
    observer = nullptr;

    return filteredImage;

}

template <class InputPixelType, class OutputPixelType, const int dim>
vtkSmartPointer<vtkImageData> MorphologicalOperators::binaryClosureFilter(vtkSmartPointer<vtkImageData> img) {

    // --------------------- Filters declaration and creation ----------------------
    // Define ITK input and output image types with respect to the instantiation
    //    types of the tamplate.
    typedef itk::Image< InputPixelType,  dim > InputImageType;
    typedef itk::Image< OutputPixelType, dim > OutputImageType;

    // Convert the image from CamiTK in VTK format to ITK format to use ITK filters.
    typedef itk::VTKImageToImageFilter<InputImageType> vtkToItkFilterType;
    typename vtkToItkFilterType::Pointer vtkToItkFilter = vtkToItkFilterType::New();

    // In the same way, once the image is filtered, we need to convert it again to
    //    VTK format to give it to CamiTK.
    typedef itk::ImageToVTKImageFilter<OutputImageType> itkToVtkFilterType;
    typename itkToVtkFilterType::Pointer itkToVtkFilter = itkToVtkFilterType::New();

    // Declare and create your own private ITK filter here...
    typedef itk::BinaryBallStructuringElement<InputPixelType, dim> StructuringElementType;
    typedef itk::BinaryErodeImageFilter <InputImageType, OutputImageType, StructuringElementType> ErosionFilterType;
    typename ErosionFilterType::Pointer erosionFilter = ErosionFilterType::New();

    typedef itk::BinaryDilateImageFilter <InputImageType, OutputImageType, StructuringElementType> DilationFilterType;
    typename DilationFilterType::Pointer dilationFilter = DilationFilterType::New();

    // To update CamiTK progress bar while filtering, add an ITK observer to the filters.
    ItkProgressObserver::Pointer observerErosion = ItkProgressObserver::New();
    ItkProgressObserver::Pointer observerDilation = ItkProgressObserver::New();
    // ITK observers generally give values between 0 and 1, and CamiTK progress bar
    //    wants values between 0 and 100...
    observerErosion->SetCoef(50.0);
    observerDilation->SetCoef(50.0);
    observerErosion->SetStartValue(50.0);


    // --------------------- Plug filters and parameters ---------------------------
    // From VTK to ITK
    vtkToItkFilter->SetInput(img);
    // For the filter itself
    StructuringElementType structuringElement;
    structuringElement.SetRadius(structuringElementSize);
    structuringElement.CreateStructuringElement();

    dilationFilter->SetInput(vtkToItkFilter->GetOutput());
    dilationFilter->AddObserver(itk::ProgressEvent(), observerDilation);
    dilationFilter->SetKernel(structuringElement);
    dilationFilter->Update();

    erosionFilter->SetInput(dilationFilter->GetOutput());
    erosionFilter->AddObserver(itk::ProgressEvent(), observerErosion);
    erosionFilter->SetKernel(structuringElement);
    erosionFilter->Update();

    // From ITK to VTK
    itkToVtkFilter->SetInput(erosionFilter->GetOutput());

    // --------------------- Actually execute all filters parts --------------------
    itkToVtkFilter->Update();


    // --------------------- Create and return a copy (the filters will be deleted)--
    vtkSmartPointer<vtkImageData> resultImage = itkToVtkFilter->GetOutput();
    vtkSmartPointer<vtkImageData> filteredImage = vtkSmartPointer<vtkImageData>::New();
    int extent[6];
    resultImage->GetExtent(extent);
    filteredImage->SetExtent(extent);
    filteredImage->DeepCopy(resultImage);

    observerErosion->Reset();
    observerDilation->Reset();

    observerDilation = nullptr;
    observerErosion = nullptr;

    return filteredImage;

}

template <class InputPixelType, class OutputPixelType, const int dim>
vtkSmartPointer<vtkImageData> MorphologicalOperators::binaryOpeningFilter(vtkSmartPointer<vtkImageData> img) {


    // --------------------- Filters declaration and creation ----------------------
    // Define ITK input and output image types with respect to the instantiation
    //    types of the tamplate.
    typedef itk::Image< InputPixelType,  dim > InputImageType;
    typedef itk::Image< OutputPixelType, dim > OutputImageType;

    // Convert the image from CamiTK in VTK format to ITK format to use ITK filters.
    typedef itk::VTKImageToImageFilter<InputImageType> vtkToItkFilterType;
    typename vtkToItkFilterType::Pointer vtkToItkFilter = vtkToItkFilterType::New();

    // In the same way, once the image is filtered, we need to convert it again to
    //    VTK format to give it to CamiTK.
    typedef itk::ImageToVTKImageFilter<OutputImageType> itkToVtkFilterType;
    typename itkToVtkFilterType::Pointer itkToVtkFilter = itkToVtkFilterType::New();

    // Declare and create your own private ITK filter here...
    typedef itk::BinaryBallStructuringElement<InputPixelType, dim> StructuringElementType;
    typedef itk::BinaryErodeImageFilter <InputImageType, OutputImageType, StructuringElementType> ErosionFilterType;
    typename ErosionFilterType::Pointer erosionFilter = ErosionFilterType::New();

    typedef itk::BinaryDilateImageFilter <InputImageType, OutputImageType, StructuringElementType> DilationFilterType;
    typename DilationFilterType::Pointer dilationFilter = DilationFilterType::New();

    // To update CamiTK progress bar while filtering, add an ITK observer to the filters.
    ItkProgressObserver::Pointer observerErosion = ItkProgressObserver::New();
    ItkProgressObserver::Pointer observerDilation = ItkProgressObserver::New();
    // ITK observers generally give values between 0 and 1, and CamiTK progress bar
    //    wants values between 0 and 100...
    observerErosion->SetCoef(50.0);
    observerDilation->SetCoef(50.0);
    observerDilation->SetStartValue(50.0);


    // --------------------- Plug filters and parameters ---------------------------
    // From VTK to ITK
    vtkToItkFilter->SetInput(img);
    // For the filter itself
    StructuringElementType structuringElement;
    structuringElement.SetRadius(structuringElementSize);
    structuringElement.CreateStructuringElement();

    erosionFilter->SetInput(vtkToItkFilter->GetOutput());
    erosionFilter->AddObserver(itk::ProgressEvent(), observerErosion);
    erosionFilter->SetKernel(structuringElement);
    erosionFilter->Update();

    dilationFilter->SetInput(erosionFilter->GetOutput());
    dilationFilter->AddObserver(itk::ProgressEvent(), observerDilation);
    dilationFilter->SetKernel(structuringElement);
    dilationFilter->Update();

    // From ITK to VTK
    itkToVtkFilter->SetInput(dilationFilter->GetOutput());

    // --------------------- Actually execute all filters parts --------------------
    itkToVtkFilter->Update();


    // --------------------- Create and return a copy (the filters will be deleted)--
    vtkSmartPointer<vtkImageData> resultImage = itkToVtkFilter->GetOutput();
    vtkSmartPointer<vtkImageData> filteredImage = vtkSmartPointer<vtkImageData>::New();
    int extent[6];
    resultImage->GetExtent(extent);
    filteredImage->SetExtent(extent);
    filteredImage->DeepCopy(resultImage);

    observerErosion->Reset();
    observerDilation->Reset();

    observerErosion = nullptr;
    observerDilation = nullptr;

    return filteredImage;

}



template <class InputPixelType, class OutputPixelType, const int dim>
vtkSmartPointer<vtkImageData> MorphologicalOperators::greyLevelErosionFilter(vtkSmartPointer<vtkImageData> img) {

    // --------------------- Filters declaration and creation ----------------------
    // Define ITK input and output image types with respect to the instantiation
    //    types of the tamplate.
    typedef itk::Image< InputPixelType,  dim > InputImageType;
    typedef itk::Image< OutputPixelType, dim > OutputImageType;

    // Convert the image from CamiTK in VTK format to ITK format to use ITK filters.
    typedef itk::VTKImageToImageFilter<InputImageType> vtkToItkFilterType;
    typename vtkToItkFilterType::Pointer vtkToItkFilter = vtkToItkFilterType::New();

    // In the same way, once the image is filtered, we need to convert it again to
    //    VTK format to give it to CamiTK.
    typedef itk::ImageToVTKImageFilter<OutputImageType> itkToVtkFilterType;
    typename itkToVtkFilterType::Pointer itkToVtkFilter = itkToVtkFilterType::New();

    // Declare and create your own private ITK filter here...
    typedef itk::BinaryBallStructuringElement<InputPixelType, dim> StructuringElementType;
    typedef itk::GrayscaleErodeImageFilter <InputImageType, OutputImageType, StructuringElementType> FilterType;
    typename FilterType::Pointer filter = FilterType::New();

    // To update CamiTK progress bar while filtering, add an ITK observer to the filters.
    ItkProgressObserver::Pointer observer = ItkProgressObserver::New();
    // ITK observers generally give values between 0 and 1, and CamiTK progress bar
    //    wants values between 0 and 100...
    observer->SetCoef(100.0);

    // --------------------- Plug filters and parameters ---------------------------
    // From VTK to ITK
    vtkToItkFilter->SetInput(img);
    // For the filter itself
    filter->SetInput(vtkToItkFilter->GetOutput());
    filter->AddObserver(itk::ProgressEvent(), observer);

    StructuringElementType structuringElement;
    structuringElement.SetRadius(structuringElementSize);
    structuringElement.CreateStructuringElement();
    filter->SetKernel(structuringElement);
    filter->Update();

    // From ITK to VTK
    itkToVtkFilter->SetInput(filter->GetOutput());

    // --------------------- Actually execute all filters parts --------------------
    itkToVtkFilter->Update();


    // --------------------- Create and return a copy (the filters will be deleted)--
    vtkSmartPointer<vtkImageData> resultImage = itkToVtkFilter->GetOutput();
    vtkSmartPointer<vtkImageData> filteredImage = vtkSmartPointer<vtkImageData>::New();
    int extent[6];
    resultImage->GetExtent(extent);
    filteredImage->SetExtent(extent);
    filteredImage->DeepCopy(resultImage);

    observer->Reset();
    observer = nullptr;

    return filteredImage;

}



template <class InputPixelType, class OutputPixelType, const int dim>
vtkSmartPointer<vtkImageData> MorphologicalOperators::greyLevelDilationFilter(vtkSmartPointer<vtkImageData> img) {

    // --------------------- Filters declaration and creation ----------------------
    // Define ITK input and output image types with respect to the instantiation
    //    types of the tamplate.
    typedef itk::Image< InputPixelType,  dim > InputImageType;
    typedef itk::Image< OutputPixelType, dim > OutputImageType;

    // Convert the image from CamiTK in VTK format to ITK format to use ITK filters.
    typedef itk::VTKImageToImageFilter<InputImageType> vtkToItkFilterType;
    typename vtkToItkFilterType::Pointer vtkToItkFilter = vtkToItkFilterType::New();

    // In the same way, once the image is filtered, we need to convert it again to
    //    VTK format to give it to CamiTK.
    typedef itk::ImageToVTKImageFilter<OutputImageType> itkToVtkFilterType;
    typename itkToVtkFilterType::Pointer itkToVtkFilter = itkToVtkFilterType::New();

    // Declare and create your own private ITK filter here...
    typedef itk::BinaryBallStructuringElement<InputPixelType, dim> StructuringElementType;
    typedef itk::GrayscaleDilateImageFilter <InputImageType, OutputImageType, StructuringElementType> FilterType;
    typename FilterType::Pointer filter = FilterType::New();

    // To update CamiTK progress bar while filtering, add an ITK observer to the filters.
    ItkProgressObserver::Pointer observer = ItkProgressObserver::New();
    // ITK observers generally give values between 0 and 1, and CamiTK progress bar
    //    wants values between 0 and 100...
    observer->SetCoef(100.0);

    // --------------------- Plug filters and parameters ---------------------------
    // From VTK to ITK
    vtkToItkFilter->SetInput(img);
    // For the filter itself
    filter->SetInput(vtkToItkFilter->GetOutput());
    filter->AddObserver(itk::ProgressEvent(), observer);

    StructuringElementType structuringElement;
    structuringElement.SetRadius(structuringElementSize);
    structuringElement.CreateStructuringElement();
    filter->SetKernel(structuringElement);
    filter->Update();

    // From ITK to VTK
    itkToVtkFilter->SetInput(filter->GetOutput());

    // --------------------- Actually execute all filters parts --------------------
    itkToVtkFilter->Update();


    // --------------------- Create and return a copy (the filters will be deleted)--
    vtkSmartPointer<vtkImageData> resultImage = itkToVtkFilter->GetOutput();
    vtkSmartPointer<vtkImageData> filteredImage = vtkSmartPointer<vtkImageData>::New();
    int extent[6];
    resultImage->GetExtent(extent);
    filteredImage->SetExtent(extent);
    filteredImage->DeepCopy(resultImage);

    observer->Reset();
    observer = nullptr;

    return filteredImage;

}

template <class InputPixelType, class OutputPixelType, const int dim>
vtkSmartPointer<vtkImageData> MorphologicalOperators::greyLevelClosureFilter(vtkSmartPointer<vtkImageData> img) {
    // --------------------- Filters declaration and creation ----------------------
    // Define ITK input and output image types with respect to the instantiation
    //    types of the tamplate.
    typedef itk::Image< InputPixelType,  dim > InputImageType;
    typedef itk::Image< OutputPixelType, dim > OutputImageType;

    // Convert the image from CamiTK in VTK format to ITK format to use ITK filters.
    typedef itk::VTKImageToImageFilter<InputImageType> vtkToItkFilterType;
    typename vtkToItkFilterType::Pointer vtkToItkFilter = vtkToItkFilterType::New();

    // In the same way, once the image is filtered, we need to convert it again to
    //    VTK format to give it to CamiTK.
    typedef itk::ImageToVTKImageFilter<OutputImageType> itkToVtkFilterType;
    typename itkToVtkFilterType::Pointer itkToVtkFilter = itkToVtkFilterType::New();

    // Declare and create your own private ITK filter here...
    typedef itk::BinaryBallStructuringElement<InputPixelType, dim> StructuringElementType;
    typedef itk::GrayscaleErodeImageFilter <InputImageType, OutputImageType, StructuringElementType> ErosionFilterType;
    typename ErosionFilterType::Pointer erosionFilter = ErosionFilterType::New();

    typedef itk::GrayscaleDilateImageFilter <InputImageType, OutputImageType, StructuringElementType> DilationFilterType;
    typename DilationFilterType::Pointer dilationFilter = DilationFilterType::New();

    // To update CamiTK progress bar while filtering, add an ITK observer to the filters.
    ItkProgressObserver::Pointer observerErosion = ItkProgressObserver::New();
    ItkProgressObserver::Pointer observerDilation = ItkProgressObserver::New();
    // ITK observers generally give values between 0 and 1, and CamiTK progress bar
    //    wants values between 0 and 100...
    observerErosion->SetCoef(50.0);
    observerDilation->SetCoef(50.0);
    observerErosion->SetStartValue(50.0);


    // --------------------- Plug filters and parameters ---------------------------
    // From VTK to ITK
    vtkToItkFilter->SetInput(img);
    // For the filter itself
    StructuringElementType structuringElement;
    structuringElement.SetRadius(structuringElementSize);
    structuringElement.CreateStructuringElement();

    dilationFilter->SetInput(vtkToItkFilter->GetOutput());
    dilationFilter->AddObserver(itk::ProgressEvent(), observerDilation);
    dilationFilter->SetKernel(structuringElement);
    dilationFilter->Update();

    erosionFilter->SetInput(dilationFilter->GetOutput());
    erosionFilter->AddObserver(itk::ProgressEvent(), observerErosion);
    erosionFilter->SetKernel(structuringElement);
    erosionFilter->Update();

    // From ITK to VTK
    itkToVtkFilter->SetInput(erosionFilter->GetOutput());

    // --------------------- Actually execute all filters parts --------------------
    itkToVtkFilter->Update();


    // --------------------- Create and return a copy (the filters will be deleted)--
    vtkSmartPointer<vtkImageData> resultImage = itkToVtkFilter->GetOutput();
    vtkSmartPointer<vtkImageData> filteredImage = vtkSmartPointer<vtkImageData>::New();
    int extent[6];
    resultImage->GetExtent(extent);
    filteredImage->SetExtent(extent);
    filteredImage->DeepCopy(resultImage);

    observerErosion->Reset();
    observerDilation->Reset();
    observerDilation = nullptr;
    observerErosion = nullptr;

    return filteredImage;

}

template <class InputPixelType, class OutputPixelType, const int dim>
vtkSmartPointer<vtkImageData> MorphologicalOperators::greyLevelOpeningFilter(vtkSmartPointer<vtkImageData> img) {

    // --------------------- Filters declaration and creation ----------------------
    // Define ITK input and output image types with respect to the instantiation
    //    types of the tamplate.
    typedef itk::Image< InputPixelType,  dim > InputImageType;
    typedef itk::Image< OutputPixelType, dim > OutputImageType;

    // Convert the image from CamiTK in VTK format to ITK format to use ITK filters.
    typedef itk::VTKImageToImageFilter<InputImageType> vtkToItkFilterType;
    typename vtkToItkFilterType::Pointer vtkToItkFilter = vtkToItkFilterType::New();

    // In the same way, once the image is filtered, we need to convert it again to
    //    VTK format to give it to CamiTK.
    typedef itk::ImageToVTKImageFilter<OutputImageType> itkToVtkFilterType;
    typename itkToVtkFilterType::Pointer itkToVtkFilter = itkToVtkFilterType::New();

    // Declare and create your own private ITK filter here...
    typedef itk::BinaryBallStructuringElement<InputPixelType, dim> StructuringElementType;
    typedef itk::GrayscaleErodeImageFilter <InputImageType, OutputImageType, StructuringElementType> ErosionFilterType;
    typename ErosionFilterType::Pointer erosionFilter = ErosionFilterType::New();

    typedef itk::GrayscaleDilateImageFilter <InputImageType, OutputImageType, StructuringElementType> DilationFilterType;
    typename DilationFilterType::Pointer dilationFilter = DilationFilterType::New();

    // To update CamiTK progress bar while filtering, add an ITK observer to the filters.
    ItkProgressObserver::Pointer observerErosion = ItkProgressObserver::New();
    ItkProgressObserver::Pointer observerDilation = ItkProgressObserver::New();
    // ITK observers generally give values between 0 and 1, and CamiTK progress bar
    //    wants values between 0 and 100...
    observerErosion->SetCoef(50.0);
    observerDilation->SetCoef(50.0);
    observerDilation->SetStartValue(50.0);


    // --------------------- Plug filters and parameters ---------------------------
    // From VTK to ITK
    vtkToItkFilter->SetInput(img);
    // For the filter itself
    StructuringElementType structuringElement;
    structuringElement.SetRadius(structuringElementSize);
    structuringElement.CreateStructuringElement();

    erosionFilter->SetInput(vtkToItkFilter->GetOutput());
    erosionFilter->AddObserver(itk::ProgressEvent(), observerErosion);
    erosionFilter->SetKernel(structuringElement);
    erosionFilter->Update();

    dilationFilter->SetInput(erosionFilter->GetOutput());
    dilationFilter->AddObserver(itk::ProgressEvent(), observerDilation);
    dilationFilter->SetKernel(structuringElement);
    dilationFilter->Update();

    // From ITK to VTK
    itkToVtkFilter->SetInput(dilationFilter->GetOutput());

    // --------------------- Actually execute all filters parts --------------------
    itkToVtkFilter->Update();


    // --------------------- Create and return a copy (the filters will be deleted)--
    vtkSmartPointer<vtkImageData> resultImage = itkToVtkFilter->GetOutput();
    vtkSmartPointer<vtkImageData> filteredImage = vtkSmartPointer<vtkImageData>::New();
    int extent[6];
    resultImage->GetExtent(extent);
    filteredImage->SetExtent(extent);
    filteredImage->DeepCopy(resultImage);

    observerErosion->Reset();
    observerDilation->Reset();
    observerDilation = nullptr;
    observerErosion = nullptr;

    return filteredImage;

}


